Scrapy是一个Python爬虫框架,可以帮助开发人员快速地构建自己的爬虫程序并从互联网上获取各种新闻、图片、视频、音乐、商品等数据。本篇文章将详细介绍如何精通Python爬虫框架Scrapy,包括原理分析、实践示例、参考资料等方面。
文章目录
一、Scrapy原理分析
Scrapy是基于Twisted异步网络框架和lxml、pyOpenSSL、cssselect等Python模块开发的。它的核心原理主要包括以下几个方面:
1.请求调度器(Request Scheduler):Scrapy的请求调度器负责管理所有的待下载请求,将其存放在队列中,并按照一定的策略分配给各个下载器进行下载。请求调度器还可以设置请求优先级、请求重试机制、请求去重等功能。
2.下载器(Downloader):Scrapy的下载器负责将请求发送到网络上,获取响应,并将响应返回给Spider进行解析。下载器还可以设置代理、超时时间、并发数量等参数。
3.爬虫(Spider):Scrapy的爬虫主要负责解析网页内容,提取需要的数据,并将提取的数据保存到数据库中或者输出到命令行等。爬虫可以使用XPath、CSS选择器、正则表达式等方式进行数据提取。
4.爬虫中间件(Spider Middleware):Scrapy的爬虫中间件可以对请求和响应进行处理,比如加入或修改请求头、响应头,自动添加cookies,自动重试失败的请求等。
5.下载器中间件(Downloader Middleware):Scrapy的下载器中间件可以对请求和响应进行处理,比如对请求进行IP代理、对响应进行解密、压缩、自动重试失败的请求等。
6.数据管道(Item Pipeline):Scrapy的数据管道主要负责将提取的数据送到持久化存储中。数据管道可以将提取的数据存储到本地文件中、数据库中、MongoDB中、Elasticsearch中等等。
二、Scrapy实践示例
为了更好的理解Scrapy的原理,接下来我们将通过一些实践示例来展示Scrapy的强大功能,包括基本用法、高级用法、定制化配置等方面。
1. Scrapy基本用法
以下是一个简单的Scrapy爬虫程序示例,可以用于爬取豆瓣电影Top250的排名、名称、评分、评分人数、导演等信息,并将爬取的信息保存到本地csv文件中。
首先,我们需要安装Scrapy模块,可以使用pip install scrapy命令进行安装。
然后,我们可以使用scrapy startproject命令创建一个Scrapy项目,如下所示:
scrapy startproject doubanmovie
这样,我们就会在当前目录下创建一个名为doubanmovie的Scrapy项目,目录结构如下:
doubanmovie/
scrapy.cfg
doubanmovie/
__init__.py
items.py
middlewares.py
pipelines.py
settings.py
spiders/
__init__.py
movie_spider.py
其中,scrapy.cfg是项目的配置文件,doubanmovie是项目的Python包,items.py、middlewares.py、pipelines.py和settings.py是项目的配置文件,spiders目录下是爬虫程序的Python脚本文件,我们可以在此目录下创建movie_spider.py文件,编写下面的代码:
import scrapy
from scrapy.selector import Selector
from doubanmovie.items import DoubanmovieItem
class MovieSpider(scrapy.Spider):
name = "doubanmovie"
allowed_domains = ["douban.com"]
start_urls = ["https://movie.douban.com/top250"]
def parse(self, response):
item = DoubanmovieItem()
movies = Selector(response).xpath('//div[@class="hd"]')
for movie in movies:
title = movie.xpath('a/span[@class="title"]/text()').extract()[0]
link = movie.xpath('a/@href').extract()[0]
item['title'] = title
item['link'] = link
yield scrapy.Request(url=link, meta={'item': item}, callback=self.parse_movie)
def parse_movie(self, response):
item = response.meta['item']
info = Selector(response).xpath('//div[@id="info"]')[0]
directors = info.xpath('span[contains(text(), "导演")]/following-sibling::*/a/text()').extract()
item['directors'] = directors
rating_score = response.xpath('//strong[@class="ll rating_num"]/text()')
rating_num = response.xpath('//div[@class="rating_sum"]/a/span/text()')
item['rating_score'] = rating_score.extract()[0] if rating_score else ""
item['rating_num'] = rating_num.extract()[1] if rating_num else ""
yield item
以上代码的意思是:首先,我们定义了一个叫做MovieSpider的爬虫程序,name为"doubanmovie",allowed_domains为"douban.com",start_urls为[“https://movie.douban.com/top250”]。
然后,我们在parse函数中使用XPath语法对响应内容进行解析,提取出电影名称和链接,并将其保存在DoubanmovieItem对象中。最后,我们使用yield关键字将电影链接作为参数发送给parse_movie函数,继续将DoubanmovieItem对象传递给它。
在parse_movie函数中,我们解析了电影详情页面中的导演、评分和评分人数,并将结果保存在DoubanmovieItem对象中。最后,我们使用yield关键字将DoubanmovieItem对象返回给Scrapy框架,Scrapy框架自动负责将结果保存到本地csv文件中。
最后,我们需要在items.py中定义DoubanmovieItem类,如下所示:
import scrapy
class DoubanmovieItem(scrapy.Item):
title = scrapy.Field()
link = scrapy.Field()
directors = scrapy.Field()
rating_score = scrapy.Field()
rating_num = scrapy.Field()
至此,我们的Scrapy爬虫程序就编写完成了。我们可以使用命令行进入doubanmovie目录,并执行以下命令运行爬虫程序:
scrapy crawl doubanmovie -o doubanmovie.csv
这样,我们就可以在项目根目录下生成一个名为doubanmovie.csv的文件,其中包含了豆瓣电影Top250的排名、名称、评分、评分人数和导演等信息。
2. Scrapy高级用法
除了基本用法之外,Scrapy还提供了一些高级用法,比如中间件机制、自定义Downloader、自定义Item Pipeline等。下面我们将分别介绍这些高级用法。
2.1 中间件机制
Scrapy使用中间件机制来处理请求和响应。中间件可以在请求和响应到达之前和之后添加或修改信息,比如IP代理、请求头、响应头、cookies等。Scrapy中的中间件可以分为Spider Middleware和Downloader Middleware两种类型。
下面是一个简单的使用中间件机制实现自动添加headers的Scrapy程序示例:
import scrapy
class HeadersMiddleware(object):
def __init__(self, headers):
self.headers = headers
def process_request(self, request, spider):
for k, v in self.headers.items():
request.headers[k] = v
class MovieSpider(scrapy.Spider):
name = "doubanmovie"
allowed_domains = ["douban.com"]
start_urls = ["https://movie.douban.com/top250"]
custom_settings = {
'DOWNLOADER_MIDDLEWARES': {
'doubanmovie.middlewares.HeadersMiddleware': 543,
}
}
def parse(self, response):
...
以上代码中,我们定义了一个叫做HeadersMiddleware的Downloader Middleware类,并实现了process_request方法,在请求前自动添加headers。
然后,在Spider中定义了一个叫做custom_settings的字典对象,其中包含了DOWNLOADER_MIDDLEWARES参数,用于指定Scrapy下载器中使用的中间件。代码中的543表示优先级,数字越小,优先级越高。
最后,我们使用命令行进行运行,并使用curl命令查看请求头:
scrapy crawl doubanmovie
curl -I https://movie.douban.com/top250
这样,我们就可以看到如下的结果:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Cache-Control: no-cache
Vary: Accept-Encoding
Strict-Transport-Security: max-age=15552000; includeSubDomains
Server: dae
Connection: keep-alive
Date: Wed, 25 Jan 2023 11:56:27 GMT
其中包含了我们自定义的请求头信息。
2.2 自定义Downloader
Scrapy的下载器默认使用了Python标准库中的urllib库和Twisted库,可以满足大部分的爬虫需求。但在某些特定情况下,比如需要使用代理IP、需要解密响应内容等,我们需要自定义Downloader。
下面是一个自定义Downloader的示例代码:
import random
import requests
from scrapy.http import Request
from scrapy.core.downloader import Downloader
from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
class HttpsProxyDownloadHandler(object):
def __init__(self, settings):
self.proxy_url = settings.get('HTTP_PROXY')
def download_request(self, request, spider):
proxies = {'http': self.proxy_url, 'https': self.proxy_url}
response = requests.get(request.url, headers=request.headers, proxies=proxies, verify=False)
response.encoding = response.apparent_encoding
respcls = spider.site.response_class
return respcls(request=request, body=response.text, status=response.status_code, headers=response.headers, url=request.url)
class CustomDownloader(Downloader):
def __init__(self, settings):
super().__init__(settings)
self.handler = HttpsProxyDownloadHandler(settings)
def download_request(self, request, spider):
return self.handler.download_request(request, spider)
class MovieSpider(scrapy.Spider):
downloader_cls = CustomDownloader
以上代码中,我们首先定义了一个叫做HttpsProxyDownloadHandler的类,用于处理带有代理IP的请求,使用requests库实现。
然后,在CustomDownloader类的构造函数中,我们使用了HttpsProxyDownloadHandler类,并重写了download_request方法,使用了自定义的Handler来处理请求。注意,我们还需要在请求头中添加headers信息,否则可能会触发网站的反爬虫机制。
最后,我们在Spider中定义了downloader_cls属性,指定使用CustomDownloader类来处理下载请求。
2.3 自定义Item Pipeline
在Scrapy中,Item Pipeline可以实现对已经爬取的数据进行多种处理,比如筛选、去重、存储、转换等等。
以下面的代码为例,我们将演示如何在Item Pipeline中实现对电影评分进行转换的功能:
from doubanmovie.items import DoubanmovieItem
class RatingTransformPipeline(object):
def process_item(self, item, spider):
if isinstance(item, DoubanmovieItem):
rating_num = int(item['rating_num'])
rating_score = float(item['rating_score'])
item['rating'] = 10 * rating_score / rating_num
return item
else:
return item
以上代码中,我们定义了一个叫做RatingTransformPipeline的Item Pipeline,用于将电影评分的分数和人数转换为相应的评分值。
在process_item方法中,我们首先使用isinstance函数判断传入的item对象是否是DoubanmovieItem类型,如果是,则进行评分转换,否则直接返回item对象。评分转换的过程为:先将评分分数和人数转换为数字类型,然后计算评分值并将其保存在item对象的rating属性中。
最后,我们可以在settings.py文件中启用这个Item Pipeline,如下所示:
ITEM_PIPELINES = {
'doubanmovie.pipelines.RatingTransformPipeline': 300,
}
这样,我们就完成了自定义Item Pipeline的操作。
三、Scrapy参考资料
Scrapy是一个强大的Python爬虫框架,可以让用户轻松地实现各种爬虫程序。下面是一些关于Scrapy学习和使用的参考资料:
-
Scrapy官方文档:https://docs.scrapy.org/en/latest/
-
Scrapy中文文档:https://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
-
Scrapy教程:https://scrapy.org/#learn-section
-
Scrapy官方示例:https://github.com/scrapy/examples
-
Scrapy中文教程:https://www.bookstack.cn/read/scrapy-doc-zh/intro-usage.md
-
Scrapy网络爬虫实战:https://www.jianshu.com/p/49c0299783fb
-
GitXiv论文爬虫实战:https://blog.csdn.net/kingjames_hwh/article/details/101482939
总之,了解Scrapy的核心原理是掌握Scrapy的关键,同时,多花时间实践,不断掌握其高级用法,并注意遵循网站的爬虫规则和法律法规,是成为Python爬虫开发工程师的必修课程。