大师兄的Python学习笔记(三十): 爬虫(十一)
大师兄的Python学习笔记(二十九): 爬虫(十)
大师兄的Python学习笔记(三十一): 爬虫(十二)
十一、Scrapy框架
6. 关于Selector
- Selector是Scrapy自己的选择器。
- Selector基于lxml,支持xpath选择器、css选择器和正则表达式。
6.1 使用方法
- 首先使用Selector模块创建一个选择器对象。
- 使用
.extract()
获取内容列表,使用.extract_first()
获取第一个匹配内容。
1) xpath选择器
- xpath选择器使用
selector.xpath()
- 在xpath选择器中添加
/text()
选取文本内容。- 在xpath选择器中添加
/@(<name>)
选取属性。>>>from scrapy import Selector >>>html = ''' >>> <html> >>> <head> >>> <title> >>> hello >>> </title> >>> </head> >>> </html>''' >>>selector = Selector(text=html) >>>title = selector.xpath('//title/text()').extract() >>>print(title) ['\n hello\n ']
2) css选择器
- css选择器使用
selector.css()
。- 在选择器中添加
::text
选取文本内容。- 在选择器中添加
::attr(<name>)
选取某个属性。>>>from scrapy import Selector >>>html = ''' >>> <html> >>> <head> >>> <title> >>> hello >>> </title> >>> </head> >>> </html>''' >>>selector = Selector(text=html) >>>title = selector.css('title::text').extract() >>>print(title) ['\n hello\n ']
3) 正则匹配
- 正则使用
selector.re()
匹配所有内容,或使用selector.re_first()
匹配第一个内容。>>>from scrapy import Selector >>>html = ''' >>> <html> >>> <head> >>> <title> >>> hello >>> </title> >>> </head> >>> </html>''' >>>selector = Selector(text=html) >>>title = selector.re('h.*o') >>>print(title) ['hello']
- 正则匹配还可以和选择器搭配使用
>>>from scrapy import Selector >>>html = ''' >>> <html> >>> <head> >>> <title> >>> hello >>> </title> >>> </head> >>> </html>''' >>>selector = Selector(text=html) >>>title = selector.css('title::text').re('e.*o') >>>print(title) ['ello']
7. 关于Spider
- 爬虫(Spider)模块负责定义爬取网站的动作,并分析爬取的网页。
7.1 爬虫的爬取流程
1) 以初始URL初始化Request,并设置回调函数
parse(self, response)
。请求成功后将response做为参数传给回调函数。2) 在回调函数中分析网页内容,内容分两类:
- 如果是有效结果,则返回Item或字典。
- 如果是下一页,则用新的URL构造request并设置新的回调函数。
2-1) 如果返回结果,可通过Feed Exports等组件将返回结果存入文件,如果设置了Pipeline,则使用Pipeline处理并保存。
2-2) 如果返回下一页的请求,则根据请求结果并执行相应的回调函数, 解析并生成Item。
重复以上步骤,直到完成爬取。
7.2 scrapy.spiders.Spider类
- Scrapy中所有的爬虫都必须继承
scrapy.spiders.Spider
类。 - scrapy.spiders.Spider类包含以下属性和方法:
属性 | 介绍 |
---|---|
name | 爬虫的名称。 |
allowed_domains | 允许爬取的域名白名单,可选。 |
start_urls | 默认起始url列表,在没有实现start_requests() 方法时从这里默认爬取。 |
custom_settings | 爬虫的配置字典,会覆盖全局设置。 |
crawler | 爬虫对应的Crawler对象,可以用来获取项目的配置信息settings 。 |
settings |
Settings 对象,可以直接获取项目的全局设置变量。 |
方法 | 介绍 |
---|---|
start_requests() | 用于生成初始请求,默认使用start_urls 里的URL构造请求,并返回一个可迭代对象。 |
parse() | 当Response 没有指定回调函数时被默认调用。负责处理返回结果,并从中提取数据和下一步请求。 返回一个包含 Request 或Item 的可迭代对象。 |
closed() | 当爬虫被关闭时调用。 |
8. 关于Downloader Middleware
- 即下载器中间件,负责处理引擎和下载器之间的请求(
requet
)和响应(response
)。
- 观察上图,下载器中间件在以下两个位置起作用:
1) 请求发送给下载器之前。
- Scheduler -> Engine -> Downloader Middlewares-> Downloader
2) 响应发送给引擎->爬虫之前。
- Downloader ->Downloader Middlewares-> Engine -> Spider
- 下载器中间件的主要功能包括: 修改User-Agent、重定向、设置代理、失败重试、Cookies等。
8.1 核心方法
- 下载器中间件有
process_request(request,spider)
、process_response(request,response,spider)
、process_exception(request,exception,spider)
三个核心方法,至少实现其中一个,即可定义一个下载器中间件。
1) process_request(request,spider)
- 在请求发送给下载器之前调用。
- 参数:
参数 说明 request 被处理的Request对象 spider 对应的爬虫对象
- 返回值必须是None、Response对象、Request对象之一, 或抛出IgnoreRequest异常:
返回值 说明 None - 继续执行其他 process_request()
方法,直到下载器获得Response。
- 本质是根据优先级修改Request。
- 最后发送给Downloader执行。Response对象 - 更低优先级的 process_request()
方法和process_exception()
方法不会被继续调用。
- 每个process_response()
方法转而被依次调用。
- 完成后将Response发送给爬虫处理。Request对象 - 更低优先级的 process_request()
方法会停止执行。
- 这个Request会作为一个全新的Request放到调度队列里,等待调度。
- 被调度后,process_request()
会被重新按照顺序执行。抛出IgnoreRequest异常 - 所有的 process_exception()
方法依次执行。
如果异常没有被处理,则使用Request的errorback()
方法回调。
如果异常还没有被处理,则忽略。2) process_response(request,response,spider)
- 在相应发送给爬虫之前调用。
- 参数:
参数 说明 request Response对应的Request对象。 response 等待被处理的Response对象。 spider 对应的爬虫对象。
- 返回值必须是Request对象、Response对象之一,或抛出IgnoreRequest异常。
返回值 说明 Request对象 - 更低优先级的 process_response()
方法不会继续调用。
- 这个Request会作为一个全新的Request放到调度队列里,等待调度。
- 被调度后,process_request()
会被按照顺序执行。Response对象 - 更低优先级的 process_response()
方法会继续调用,对该Response对象进行处理。抛出IgnoreRequest异常 - Request的 errorback()
方法回调。
- 如果异常还没有被处理,则忽略。3) process_exception(request,exception,spider)
- 当下载器或
process_request()
方法抛出异常时被调用。- 参数:
参数 说明 request 产生异常的Request对象。 exception 被抛出的Exception对象。 spider 对应的爬虫对象。
- 返回值必须是None、Response对象、Request对象之一。
参数 说明 None - 更低优先级的 process_exception()
会被继续按顺序调用,直到所有方法都被调度完毕。Response对象 - 更低优先级的 process_exception()
不再被继续调用。
-process_response()
方法装而被依次调用。Request对象 - 更低优先级的 process_exception()
不再被继续调用。
- 这个Request会作为一个全新的Request放到调度队列里,等待调度。
- 被调度后,process_request()
会被按照顺序执行。
- 使用前需要在
settings.py
中配置中间件:
>>>DOWNLOADER_MIDDLEWARES = {
>>> 'project_sample.middlewares.ProjectSampleDownloaderMiddleware': 543,
>>>}
- 一个简单案例,从settings中读取并修改request的user_agent:
>>>class ProjectSampleDownloaderMiddleware(object):
>>> def __init__(self,user_agent):
>>> self.user_agent=user_agent
>>> @classmethod
>>> def from_crawler(cls, crawler):
>>> s = cls(crawler.settings['USER_AGENT'])
>>> crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
>>> return s
>>> def process_request(self, request, spider):
>>> if self.user_agent:
>>> request.headers.setdefault(b'User-Agent',self.user_agent)
>>> return None
>>> def spider_opened(self, spider):
>>> self.user_agent = getattr(spider,'user_agent',self.user_agent)
9. 关于Spider Middleware
-
即爬虫中间件,位于引擎和爬虫间的Hook框架,主要处理向爬虫输入的响应和输出的结果及新的请求。
- 主要在以下三个位置起作用:
1) 下载器将生成的响应发送给爬虫之前
- Downloader -> Engine -> Spider Middlewares -> Spiders
2) 爬虫将请求发送给Scheduler之前
- Spider -> Spider Middlewares ->Engine -> Scheduler
3) 爬虫将生成的项目发给Item Pipeline之前
Spider -> Spider Middlewares ->Engine -> Item Pipeline
9.1 核心方法
- 爬虫中间件的核心方法有:
process_spider_input(response,spider)
、process_spider_output(response,result,spider)
、process_spider_exception(response,exception,spider)
、process_start_requests(start_requests,spider)
。
1) process_spider_input(response,spider)
- 当爬虫中间件处理response时,该方法会被调用。
- 参数:
参数 说明 response 将被处理的Response对象。 spider 对应的爬虫对象。
- 返回值应该是None或者抛出异常:
返回值 说明 None 调用其它中间件,直到Spider处理这个Response。 抛出异常 - 不会调用其它的爬虫中间件,而是调用 errback()
方法。
-errback()
的输出使用process_spider_output()
方法处理。
- 异常调用process_spider_exception()
处理。2) process_spider_output(response,result,spider)
- 当爬虫处理Response返回结果时调用。
- 参数:
参数 说明 response 将被处理的Response对象。 result 爬虫的返回结果,包含Request或Item的可迭代对象。 spider 对应的爬虫对象。
- 返回值是处理过的result,必须包含Request或item的可迭代对象。
3) process_spider_exception(response,exception,spider)
- 当
process_spider_input()
抛出异常时调用。- 参数:
参数 说明 response 将被处理的Response对象。 exception 被抛出的Exception对象。 spider 对应的爬虫对象。
- 返回值应该是None或者包含Response/Item的可迭代对象:
返回值 说明 None 继续调用其它 process_spider_exception()
方法,直到全部被调用。Response/Item可迭代对象 调用 process_spider_output()
方法。4) process_start_requests(start_requests,spider)
- 当爬虫发起参数时被调用。
参数 说明 start_requests 包含Request的可迭代对象 spider 对应的爬虫对象。
- 返回处理过的start_requests,也就是包含Request的可迭代对象。
- 使用前需要先在
settings.py
配置中间件:
>>>SPIDER_MIDDLEWARES = {
>>>> 'project_sample.middlewares.ProjectSampleSpiderMiddleware': 543,
>>>}
- 简单案例
class ProjectSampleSpiderMiddleware(object):
def process_spider_output(self, response, result, spider):
for i in result:
if isinstance(i, scrapy.Item):
print('现在将把Item发送到pipeline')
yield i
... ...
现在将把Item发送到pipeline
2020-09-15 11:12:22 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/26357307/>
{'actors': 27,
'director': '妮琪·卡罗',
'title': '花木兰 Mulan',
'url': 'https://movie.douban.com/subject/26357307/'}
... ...
10. 关于Item Pipeline
- Item Pipeline即项目管道,接收并处理爬虫返回的Item(项目)。
- Item Pipeline常用的操作有:
1) 清理HTML数据。
2) 验证爬取数据,检查爬取字段。
3) 查重并丢弃重复内容。
4) 将爬取结果保存到数据库。
10.1 使用Item Pipeline
- Item Pipeline即项目管道,主要在爬虫流程的结尾部分生效:Spider -> Engine -> Item Pipeline
- 使用Item Pipeline,必须在
pipelines.py
文件的类中添加一个process_item(item,spider)
方法,除此之外还有open_spider(spider)
、close_spider(spider)
、from_crawler(cls,crawler)
等方法。
1) process_item(item,spider)
- 定义后的Item Pipeline会默认调用此方法。
- 参数:
参数 说明 item 爬虫生成的Item对象。 spider 对应的爬虫。
- 可以返回Item对象或抛出DropItem异常:
返回值 说明 Item对象 所有Item Pipeline按优先级继续处理。 抛出DropItem异常 Item对象被丢弃,不在进行处理。 2) open_spider(spider)
- 在爬虫开启时被自动调用, 通常用来执行初始化工作。
- 参数spider是相应的爬虫对象。
3) close_spider(spider)
- 在爬虫关闭时被自动调用, 通常用来执行收尾工作。
- 参数spider是相应的爬虫对象。
4) from_crawler(cls,crawler)
from_crawler()
是类方法, 用@classmethod标识。- 类方法的第一个参数(cls)必须是类的实例。
- 参数crawler是crawler对象,可以用于获得scrapy的全局配置信息。
- 简单案例:
>>>from scrapy.exceptions import DropItem
>>>class ProjectSamplePipeline(object):
>>> def process_item(self, item, spider):
>>> # 将演员名称替换为演员数量
>>> if item.get('actors'):
>>> item['actors'] = len(item['actors'])
>>> return item
>>> else:
>>> return DropItem("No actors")
- 在使用前还需要在
settings.py
中配置item pipeline, 数字代表执行优先级权重:
>>># Configure item pipelines
>>># See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
>>>ITEM_PIPELINES = {
>>> 'project_sample.pipelines.ProjectSamplePipeline': 300,
>>>}
- 运行爬虫查看结果,演员名称已经被替换为演员数:
D:\project_sample>scrapy crawl douban
... ...
{'actors': 18,
'director': '泰特·泰勒',
'title': '艾娃 Ava',
'url': 'https://movie.douban.com/subject/30289869/'}
2020-09-10 09:56:46 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://movie.douban.com/subject/30129061/> (referer: https://movie.douban.com/chart)
2020-09-10 09:56:46 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://movie.douban.com/subject/27131969/> (referer: https://movie.douban.com/chart)
2020-09-10 09:56:46 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://movie.douban.com/subject/34462775/> (referer: https://movie.douban.com/chart)
2020-09-10 09:56:46 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/35141706/>
{'actors': 10,
'director': '周润泽',
'title': '东北往事:我叫刘海柱',
'url': 'https://movie.douban.com/subject/35141706/'}
2020-09-10 09:56:46 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/30330875/>
{'actors': 27,
'director': '亨利·朱斯特',
'title': '超能计划 Project Power',
'url': 'https://movie.douban.com/subject/30330875/'}
2020-09-10 09:56:47 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/30479644/>
{'actors': 27,
'director': '杨宇硕',
'title': '铁雨2:首脑峰会 강철비2: 정상회담',
'url': 'https://movie.douban.com/subject/30479644/'}
2020-09-10 09:56:47 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/26389321/>
{'actors': 14,
'director': '约什·布恩',
'title': '新变种人 The New Mutants',
'url': 'https://movie.douban.com/subject/26389321/'}
2020-09-10 09:56:47 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/30129061/>
{'actors': 15,
'director': '查理·考夫曼',
'title': "我想结束这一切 I'm Thinking of Ending Things",
'url': 'https://movie.douban.com/subject/30129061/'}
2020-09-10 09:56:47 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/27131969/>
{'actors': 6,
'director': '李云波',
'title': '无名狂',
'url': 'https://movie.douban.com/subject/27131969/'}
2020-09-10 09:56:47 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/34462775/>
{'actors': 2,
'director': '赵一亨',
'title': '#活着 #살아있다',
'url': 'https://movie.douban.com/subject/34462775/'}
2020-09-10 09:56:47 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://movie.douban.com/subject/26357307/> (referer: https://movie.douban.com/chart)
2020-09-10 09:56:47 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://movie.douban.com/subject/30299515/> (referer: https://movie.douban.com/chart)
2020-09-10 09:56:47 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/26357307/>
{'actors': 27,
'director': '妮琪·卡罗',
'title': '花木兰 Mulan',
'url': 'https://movie.douban.com/subject/26357307/'}
2020-09-10 09:56:47 [scrapy.core.scraper] DEBUG: Scraped from <200 https://movie.douban.com/subject/30299515/>
{'actors': 8,
'director': '延尚昊',
'title': '釜山行2:半岛 부산행2-반도',
'url': 'https://movie.douban.com/subject/30299515/'}
... ...
10.2 将数据保存到数据库
- 首先需要在
settings.py
中配置Item pipeline和数据库信息。
>>># Configure item pipelines
>>># See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
>>>ITEM_PIPELINES = {
>>> 'project_sample.pipelines.ProjectSamplePipeline': 300,
>>> 'project_sample.pipelines.MongoDBPipeline': 400,
>>>}
>>>MONGO_URI='localhost'
>>>MONGO_DB='project'
- 在
pipelines.py
文件中增加一个Item pipeline类:
>>>import pymongo
>>>class MongoDBPipeline(object):
>>> def __init__(self,mongo_uri,mongo_db):
>>> self.mongo_uri = mongo_uri
>>> self.mongo_db = mongo_db
>>> @classmethod
>>> def from_crawler(cls,crawler):
>>> # 类方法,用于获取settings中的全局的配置信息
>>> return cls(
>>> mongo_uri=crawler.settings.get('MONGO_URI'),
>>> mongo_db=crawler.settings.get('MONGO_DB')
>>> )
>>> def open_spider(self,spider):
>>> '''
>>> spider开启时调用
>>> 用于初始化mongodb
>>> '''
>>> self.client = pymongo.MongoClient(self.mongo_uri)
>>> self.db = self.client[self.mongo_db]
>>> def close_spider(self,spider):
>>> '''
>>> spider结束时调用
>>> 用于关闭数据库链接
>>> '''
>>> self.client.close()
>>> def process_item(self, item, spider):
>>> name = item.__class__.__name__
>>> self.db[name].insert(dict(item))
>>> return item
- 运行爬虫后,在数据库中查询:
> db.ProjectSampleItem.find()
{ "_id" : ObjectId("5f59929c69d6a3f8e7263087"), "url" : "https://movie.douban.com/subject/35141706/", "title" : "东北往事:我叫刘海柱", "actors" : 10, "director" : "周润泽" }
{ "_id" : ObjectId("5f59929c69d6a3f8e7263088"), "url" : "https://movie.douban.com/subject/34462775/", "title" : "#活着 #살아있다", "actors" : 2, "director" : "赵一亨" }
{ "_id" : ObjectId("5f59929c69d6a3f8e7263089"), "url" : "https://movie.douban.com/subject/30289869/", "title" : "艾娃 Ava", "actors" : 18, "director" : "泰特·泰勒" }
{ "_id" : ObjectId("5f59929c69d6a3f8e726308a"), "url" : "https://movie.douban.com/subject/26389321/", "title" : "新变种人 The New Mutants", "actors" : 14, "director" : "约什·布恩" }
{ "_id" : ObjectId("5f59929c69d6a3f8e726308b"), "url" : "https://movie.douban.com/subject/30330875/", "title" : "超能计划 Project Power", "actors" : 27, "director" : "亨利·朱斯特" }
{ "_id" : ObjectId("5f59929c69d6a3f8e726308c"), "url" : "https://movie.douban.com/subject/30479644/", "title" : "铁雨2:首脑峰会 강철비2: 정상회담", "actors" : 27, "director" : "杨宇硕" }
{ "_id" : ObjectId("5f59929c69d6a3f8e726308d"), "url" : "https://movie.douban.com/subject/27131969/", "title" : "无名狂", "actors" : 6, "director" : "李云波" }
{ "_id" : ObjectId("5f59929c69d6a3f8e726308e"), "url" : "https://movie.douban.com/subject/30129061/", "title" : "我想结束这一切 I'm Thinking of Ending Things", "actors" : 15, "director" : "查理·考夫曼" }
{ "_id" : ObjectId("5f59929d69d6a3f8e726308f"), "url" : "https://movie.douban.com/subject/30299515/", "title" : "釜山行2:半岛 부산행2-반도", "actors" : 8, "director" : "延尚昊" }
{ "_id" : ObjectId("5f59929d69d6a3f8e7263090"), "url" : "https://movie.douban.com/subject/26357307/", "title" : "花木兰 Mulan", "actors" : 27, "director" : "妮琪·卡罗" }
{ "_id" : ObjectId("5f5995018155a8757b4937eb"), "url" : "https://movie.douban.com/subject/30479644/", "title" : "铁雨2:首脑峰会 강철비2: 정상회담", "actors" : 27, "director" : "杨宇硕" }
{ "_id" : ObjectId("5f5995018155a8757b4937ec"), "url" : "https://movie.douban.com/subject/34462775/", "title" : "#活着 #살아있다", "actors" : 2, "director" : "赵一亨" }
{ "_id" : ObjectId("5f5995018155a8757b4937ed"), "url" : "https://movie.douban.com/subject/30330875/", "title" : "超能计划 Project Power", "actors" : 27, "director" : "亨利·朱斯特" }
{ "_id" : ObjectId("5f5995018155a8757b4937ee"), "url" : "https://movie.douban.com/subject/30129061/", "title" : "我想结束这一切 I'm Thinking of Ending Things", "actors" : 15, "director" : "查理·考夫曼" }
{ "_id" : ObjectId("5f5995018155a8757b4937ef"), "url" : "https://movie.douban.com/subject/27131969/", "title" : "无名狂", "actors" : 6, "director" : "李云波" }
{ "_id" : ObjectId("5f5995018155a8757b4937f0"), "url" : "https://movie.douban.com/subject/30289869/", "title" : "艾娃 Ava", "actors" : 18, "director" : "泰特·泰勒" }
{ "_id" : ObjectId("5f5995018155a8757b4937f1"), "url" : "https://movie.douban.com/subject/35141706/", "title" : "东北往事:我叫刘海柱", "actors" : 10, "director" : "周润泽" }
{ "_id" : ObjectId("5f5995018155a8757b4937f2"), "url" : "https://movie.douban.com/subject/26389321/", "title" : "新变种人 The New Mutants", "actors" : 14, "director" : "约什·布恩" }
{ "_id" : ObjectId("5f5995018155a8757b4937f3"), "url" : "https://movie.douban.com/subject/30299515/", "title" : "釜山行2:半岛 부산행2-반도", "actors" : 8, "director" : "延尚昊" }
{ "_id" : ObjectId("5f5995018155a8757b4937f4"), "url" : "https://movie.douban.com/subject/26357307/", "title" : "花木兰 Mulan", "actors" : 27, "director" : "妮琪·卡罗" }
参考资料
- https://blog.csdn.net/u010138758/article/details/80152151 J-Ombudsman
- https://www.cnblogs.com/zhuluqing/p/8832205.html moisiet
- https://www.runoob.com 菜鸟教程
- http://www.tulingxueyuan.com/ 北京图灵学院
- http://www.imooc.com/article/19184?block_id=tuijian_wz#child_5_1 两点水
- https://blog.csdn.net/weixin_44213550/article/details/91346411 python老菜鸟
- https://realpython.com/python-string-formatting/ Dan Bader
- https://www.liaoxuefeng.com/ 廖雪峰
- https://blog.csdn.net/Gnewocean/article/details/85319590 新海说
- https://www.cnblogs.com/Nicholas0707/p/9021672.html Nicholas
- https://www.cnblogs.com/dalaoban/p/9331113.html 超天大圣
- https://blog.csdn.net/zhubao124/article/details/81662775 zhubao124
- https://blog.csdn.net/z59d8m6e40/article/details/72871485 z59d8m6e40
- https://www.jianshu.com/p/2b04f5eb5785 MR_ChanHwang
- 《Python学习手册》Mark Lutz
- 《Python编程 从入门到实践》Eric Matthes
- 《Python3网络爬虫开发实战》崔庆才
本文作者:大师兄(superkmi)