视频教学网址:【python爬虫_从入门到精通(高级篇)】scrapy框架、反爬、分布式爬虫
具体的创建方法可以参照上一篇文章Python最火爬虫框架Scrapy入门与实践
创建的目录结构如下:
在qsbk_spider.py文件中,response是一个’scrapy.http.response.html.HtmlResponse’对象.可以执行’xpath’和’css’语法来提取数据。
# -*- coding: utf-8 -*-
import scrapy
from qsbk.items import QsbkItem
class QsbkSpiderSpider(scrapy.Spider):
name = 'qsbk_spider'
allowed_domains = ['qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/page/1/']
def parse(self, response):
# SelectorList
duanzidivs = response.xpath("//div[@class='col1 old-style-col1']/div")
# 遍历div
for duanzidiv in duanzidivs:
# Selector-->Unicode
author = duanzidiv.xpath(".//a/h2/text()").get().strip()
content = duanzidiv.xpath(".//div[@class='content']//text()").getall()
content = " ".join(content).strip()
# 提取出来的数据,是一个’Selector’或者是一个’SelectorList’对象。如果想要获取其中的字符串,那么应该执行’getall’或是’get’方法
# getall方法:获取Selector中的所有文本,返回的是一个列表。get方法:是获取selector中的第一个文本,返回的是str类型.
item = QsbkItem(author=author,content=content)
#如果数据解析回来,要传给pipline处理.那么可以使用’yield’来返.或是可以收集到所有的item,最后统一使用return返回.
yield item
在itmes.py文件中,用来存放爬虫爬取下来的数据类型,里面的数据与qsbk_spider.py 定义的 一 一对应。
import scrapy
# item:建议在item.py中定义好模型.以后就不要使用字典了.在最后将数据变成字典就行了
class QsbkItem(scrapy.Item):
author = scrapy.Field()
content = scrapy.Field()
在piplines文件中,用来将items的模型存储到本地磁盘中;
pipeline:这个是专门用来保存数据地.其中三个方法是会经常用到的
①open_spider(self,spider):当爬虫被打开的时候就会被调用
②process_item(self,item,spider):当爬虫有item传过来的时候就会被调用
③close_spider(self,spider):当爬虫关闭的时候会被调用
要激活piplilne , 应该在settings.py中设置ITEM_PIPELINES将其激活.
import json
class QsbkPipeline(object):
def __init__(self):
self.fp = open("duanzi.json",'w',encoding="utf-8") # 创建一个导入的对象
def open_spider(self,spider):
print("爬虫开始了。。")
def process_item(self, item, spider):
# 将item转换为字典,再将字典转换为json ;item是爬虫返回的数据
item_json = json.dumps(dict(item),ensure_ascii=False)
# json字符串写入文件当中
self.fp.write(item_json+'\n')
return item
def close_spider(self,spider):
self.fp.close()
print("爬虫结束了。。")
settings.py: 本爬虫的一些配置信息(比如请求头、多久发送一次、ip代理池等等),需要将下面的注释去掉。
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36 SLBrowser/6.0.1.4221'
}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'qsbk.pipelines.QsbkPipeline': 300, #值越小优先级越高;当设置多个爬虫时,可以参考此值。
}
由于每一次在命令端调试比较麻烦,可以在qsbk文件的根目录创建start.py文件进行调试。
from scrapy import cmdline
# 调式
cmdline.execute("scrapy crawl qsbk_spider".split())
# cmdline.execute(['scrapy', 'crawl', 'qsbk_spider'])
在pipelines.py文件里保存json数据的时候,可以使用这两个类,让操作更加简单。
①JsonItemExporter:这个是每次把数据添加到内存中.最后统一写入到磁盘中.
好处就是存储的数据是一个满足json规则的数据.
坏处就是如果数据量比较大,那么比较消耗内存
from scrapy.exporters import JsonItemExporter
class QsbkPipeline(object):
def __init__(self):
# 二进制打开文件,因为JsonItemExporter是以二进制写入写入的
self.fp = open("duanzi.json",'wb')
self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding="utf-8")
# 开始导入
self.exporter.start_exporting()
def open_spider(self,spider):
print("爬虫开始了。。")
def process_item(self, item, spider):
# 先把传进来的字典塞到一个列表当中
self.exporter.export_item(item)
return item
def close_spider(self,spider):
# 完成导入
self.exporter.finish_exporting() # 再统一写道json文件中
json文件存储一行数据:
[{"author": "山鹰寂寞飞", "content": "那年那人那山第三十二章:钱去楼空 红军很惊讶的问:“怎么了?是怪我昨天没来接你吗?”秀芬面无表情,眼角泪珠往下飞速滚淌着。 红军凑上前,蹲下握住秀芬的手道:“好了,我知道错了,下次不管再晚,哪怕骑车也过来接你,好不好?” 秀芬触电一般用力拽回自己的手,歇斯底里大叫一句:“你走啊!我不想再看到你!” 红军吓了一跳,见秀芬如此激动,好像真想分手,有点莫名其妙,他盯了秀芬一会,眼见她侧脸昂头,只流泪不再说话,态度相当坚决,思忖一会问:“为什么?总得给我个理由吧?是…因为我没\n…\n \n\n 查看全文"}]......
②JsonLinesItemExporter:这个是每次调用export_item的时候就吧这个item存储到硬盘当中.
坏处是每一个字典是一行,整个文件不是一个满足json格式的文件.
好处就是每次处理数据的时候就直接存储到硬盘当中,这样不会消耗内存,数据也比较安全.
from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline(object):
def __init__(self):
# 二进制打开文件,因为JsonItemExporter是以二进制写入写入的
self.fp = open("duanzi.json",'wb')
self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding="utf-8")
def open_spider(self,spider):
print("爬虫开始了。。")
def process_item(self, item, spider):
# 先把传进来的字典塞到一个列表当中
self.exporter.export_item(item)
return item
def close_spider(self,spider):
self.fp.close()
print("爬虫结束了。。")
输出结果:一个字典一行的存储:
{"author": "山鹰寂寞飞", "content": "那年那人那山第三十三章:炼狱化疗 传说中的化疗很快来到,秀芬和红军以为是什么工程浩大惊天动地的疗法,没成想也就是几瓶药水输进身体。 然而他们都小瞧了这几瓶药水的威力,刚挂上没一会,楚建国开始恶心,说胃里火烧火燎像有硫酸在腐蚀,不停的干呕着,后来实在憋持不住,让秀芬赶紧拿来垃圾桶,他侧倒在床沿翻江倒海的抽搐着身子,一阵又一阵吐得眼泪直流。 早上吃的东西全都吐了个干净,顺着嘴角又一佝偻一佝偻呕出黄疸水,偌大的一个壮汉,竟然像个孩子似的,吐的哭出了声音。 秀芬很害怕,去找值班医生,\n…\n \n\n 查看全文"}
{"author": "五少爷的刀", "content": "中午在食堂吃午饭的时候,刚好和厂长坐在一起,为了活跃气氛,我就讲了两个笑话。 厂长果然被我逗笑了,噗~的一声,把他刚扒进嘴里的饭全喷在我碗里了。。。 大家都放下了碗筷,扭头看我。 就在我在考虑是不是不要了的时间,厂长带的二哈跳起来把我的饭吃了!…… 谢天谢地啊,谢天谢地!!!……我站起来,朝着二哈,一躬到地。。。"}
{"author": "精灵不次饭~", "content": "穿了条新裙子,照镜子觉得特别好看。我脑子一抽就发视频给在玩游戏的老公,问他好看不,还跟他卖个萌做了个鬼脸。 现在我很想把他踹出家门!!这货不停的追问我“你直接喊我来看不就行了吗?”“我在家呢,你发视频干啥?”“你是不是发错人了?” “你本来打算发给谁的?”"}
.......
修改qsbk_spider.py,分析"下一页"按钮的标签结构
# -*- coding: utf-8 -*-
import scrapy
from qsbk.items import QsbkItem
class QsbkSpiderSpider(scrapy.Spider):
name = 'qsbk_spider'
allowed_domains = ['qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/page/1/']
base_domain = "https://www.qiushibaike.com"
def parse(self, response):
# SelectorList
duanzidivs = response.xpath("//div[@class='col1 old-style-col1']/div")
# 遍历div
for duanzidiv in duanzidivs:
# Selector-->
author = duanzidiv.xpath(".//a/h2/text()").get().strip()
content = duanzidiv.xpath(".//div[@class='content']//text()").getall()
content = " ".join(content).strip()
item = QsbkItem(author=author,content=content)
yield item
next_url = response.xpath("//ul[@class='pagination']/li[last()]/a/@href").get()
if not next_url:
return
else:
yield scrapy.Request(self.base_domain+next_url,callback=self.parse)
# 返回当前的请求
在setting文件中,找到下面的注释掉,可以调成一秒获取一次数据
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 1