Scrapy框架
Scrapy是用Python实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘、信息处理或存储历史数据等一系列的程序中。
Scrapy使用Twisted基于事件的高效异步网络框架来处理网络通信,可以加快下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。
Scrapy架构
Scrapy Engine
调度器(Scheduler)
下载器(Downloader)
Spiders爬虫
Spider是编写的类,作用如下:
Item Pipeline
Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)
当页面被爬虫解析所需的数据存入Item后,将被发送到项目管道(Pipeline),并经过设置好次序的pipeline程序处理这些数据,最后将存入本地文件或存入数据库
类似管道 $ ls | grep test 或者类似于Django 模板中的过滤器
以下是item pipeline的一些典型应用:
清理HTML数据
验证爬取的数据(检查item包含某些字段)
查重(或丢弃)
将爬取结果保存到数据库中
下载器中间件(Downloader middlewares)
简单讲就是自定义扩展下载功能的组件。
下载器中间件,是在引擎和下载器之间的特定钩子(specific hook),处理它们之间的请求request和响应response。
它提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能
通过设置下载器中间件可以实现爬虫自动更换user-agent、IP等功能
Spider中间件(Spider middlewares)
Spider中间件,是在引擎和Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items或requests)。
也提供了同样的简便机制,通过插入自定义代码来扩展Scrapy功能。
数据流(Data flow)
引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个(批)要爬取的URL(s)
引擎从Spider中获取到第一个要爬取的URL并加入到调度器(Scheduler)作为请求以备调度
引擎向调度器请求下一个要爬取的URL
调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件并转发给下载器(Downloader)
一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件发送给引擎
引擎从下载器中接收到Response,然后通过Spider中间件发送给Spider处理
Spider处理Response并返回提取到的Item及(跟进的)新的Request给引擎
引擎将Spider返回的Item交给Item Pipeline,将Spider返回的Request交给调度器
(从第二步)重复执行,直到调度器中没有待处理的request,引擎关闭
注意:
只有当调度器中没有任何request了,整个程序才会停止执行。如果有下载失败的URL,会重新下载
安装scrapy
windows下出现如下问题:
copying src\twisted\words\xish\xpathparser.g -> build\lib.win-amd64-3.5\twisted\words\xish
running build_ext
building 'twisted.test.raiser' extension
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build
Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools
解决方案是,下载编译好的twisted,https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
python3.5 下载 Twisted-18.4.0-cp35-cp35m-win_amd64.whl
python3.6 下载 Twisted-18.4.0-cp36-cp36m-win_amd64.whl
安装twisted
$ pip install Twisted-18.4.0-cp35-cp35m-win_amd64.whl
之后在安装scrapy就没有什么问题了
安装好,使用scrapy命令看看
> scrapy
Scrapy 1.5.0 - no active project
Usage:
scrapy [options] [args]
Available commands:
bench Run quick benchmark test
check Check spider contracts
crawl Run a spider
edit Edit spider
fetch Fetch a URL using the Scrapy downloader
genspider Generate new spider using pre-defined templates
list List available spiders
parse Parse URL (using its spider) and print the results
runspider Run a self-contained spider (without creating a project)
settings Get settings values
shell Interactive scraping console
startproject Create new project
version Print Scrapy version
view Open URL in browser, as seen by Scrapy
Scrapy开发
项目编写流程
创建项目
使用 scrapy startproject proname 创建一个scrapy项目
scrapy startproject [project_dir]
编写item
在items.py中编写Item类,明确从response中提取的item
编写爬虫
编写spiders/proname_spider.py,即爬取网站的spider并提取出item
编写item pipeline
item的处理,可以存储
1 创建项目
1.1 豆瓣书评爬取
标签为“编程”,第一页、第二页链接:
https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T
https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=20&type=T
随便找一个目录来创建项目,执行下面命令
$ scrapy startproject first .
会产生如下目录和文件
first
├─ scrapy.cfg
└─ first
├─ items.py
├─ middlewares.py
├─ pipelines.py
├─ settings.py
├─ __init__.py
└─ spiders
└─ __init__.py
first:
外部的first目录是整个项目目录,内部的first目录是整个项目的全局目录
scrapy.cfg:
必须有的重要的项目的配置文件
first 项目目录
__init__.py 必须有,包文件
items.py 定义Item类,从scrapy.Item继承,里面定义scrapy.Field类实例
pipelines.py 重要的是process_item()方法,处理item
settings.py:
BOT_NAME 爬虫名
ROBOTSTXT_OBEY = True 是否遵从robots协议
USER_AGENT = '' 指定爬取时使用
CONCURRENT_REQEUST = 16 默认16个并行
DOWNLOAD_DELAY = 3 下载延时,一般要设置,不宜过快发起连续请求
COOKIES_ENABLED = False 缺省是启用,一般需要登录时才需要开启cookie
SPIDER_MIDDLEWARES 爬虫中间件
DOWNLOADER_MIDDLEWARES 下载中间件
ITEM_PIPELINES 管道配置
spiders目录
__init__.py 必须有,可以在这里写爬虫类,也可以写爬虫子模块
BOT_NAME = 'first'
SPIDER_MODULES = ['first.spiders']
NEWSPIDER_MODULE = 'first.spiders'
USER_AGENT = "Mozilla/5.0 (Windows NT 6.1)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 3
COOKIES_ENABLED = False
注意一定要更改User-Agent,否则访问 https://book.douban.com/ 会返回403
2 编写Item
在items.py中编写
import scrapy
class BookItem(scrapy.Item):
title = scrapy.Field()
rate = scrapy.Field()
3 编写爬虫
为爬取豆瓣书评编写爬虫类,在spiders目录下:
class Spider():
def parse(self, response):
raise NotImplementedError
爬取读书频道,tag为“编程”的书名和评分:
https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=20&type=T
使用模板创建spider, $ scrapy genspider -t basic book douban.com
import scrapy
class BookSpider(scrapy.Spider):
name = 'doubanbook'
allowed_domains = ['douban.com']
url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'
start_urls = [url]
def parse(self, response):
print(type(response), '~~~~~~~~~')
print(response)
print('-' * 30)
使用crawl爬取子命令
$ scrapy list
$ scrapy crawl -h
scrapy crawl [options]
指定爬虫名称开始爬取
$ scrapy crawl doubanbook
可以不打印日志
$ scrapy crawl doubanbook --nolog
如果在windows下运行发生twisted的异常 ModuleNotFoundError: No module named 'win32api' ,请安装 $ pip install pywin32。
response是服务器端HTTP响应,它是scrapy.http.response.html.HtmlResponse类。
由此,修改代码如下
import scrapy
from scrapy.http.response.html import HtmlResponse
class BookSpider(scrapy.Spider):
name = 'doubanbook'
allowed_domains = ['douban.com']
url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'
start_urls = [url]
def parse(self, response:HtmlResponse):
print(type(response))
print('-'*30)
print(type(response.text), type(response.body))
print('-'*30)
print(response.encoding)
with open('o:/testbook.html', 'w', encoding='utf-8') as f:
try:
f.write(response.text)
f.flush()
except Exception as e:
print(e)
3.1 解析HTML
爬虫获得的内容response对象,可以使用解析库来解析。
scrapy包装了lxml,父类TextResponse类也提供了xpath方法和css方法,可以混合使用这两套接口解析HTML。
选择器参考:
https://scrapy-chs.readthedocs.io/zh_CN/0.24/topics/selectors.html#id3
import scrapy
from scrapy.http.response.html import HtmlResponse
response = HtmlResponse('file:///O:/testbook.html', encoding='utf-8')
with open('o:/testbook.html', encoding='utf8') as f:
response._set_body(f.read())
subjects = response.xpath('//li[@class="subject-item"]')
for subject in subjects:
title = subject.xpath('.//h2/a/text()').extract()
print(title[0].strip())
rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract()
print(rate[0].strip())
print('-'*30)
subjects = response.css('li.subject-item')
for subject in subjects:
title = subject.css('h2 a::text').extract()
print(title[0].strip())
rate = subject.css('span.rating_nums::text').extract()
print(rate[0].strip())
print('-'*30)
subjects = response.css('li.subject-item')
for subject in subjects:
href =subject.xpath('.//h2').css('a::attr(href)').extract()
print(href[0])
id = subject.xpath('.//h2/a/@href').re(r'\d*99\d*')
if id:
print(id[0])
rate = subject.xpath('.//span[@class="rating_nums"]/text()').re(r'^9.*')
if rate:
print(rate)
3.2 item封装数据
import scrapy
from scrapy.http.response.html import HtmlResponse
from ..items import BookItem
class BookSpider(scrapy.Spider):
name = 'doubanbook'
allowed_domains = ['douban.com']
url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'
start_urls = [url]
def parse(self, response:HtmlResponse):
items = []
subjects = response.xpath('//li[@class="subject-item"]')
for subject in subjects:
title = subject.xpath('.//h2/a/text()').extract()
rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract_first()
item = BookItem()
item['title'] = title[0].strip()
item['rate'] = rate.strip()
items.append(item)
print(items)
return items
得到下图数
注意上图的数据已经是unicode字符,汉字的unicode表达。
4 pipeline处理
将bookspider.py中BookSpider改成生成器,只需要把 return items 改造成 yield item ,即由产生一个列表变成yield一个个item
脚手架帮我们创建了一个pipelines.py文件和一个类
4.1 开启pipeline
ITEM_PIPELINES = {
'first.pipelines.FirstPipeline': 300,
}
整数300表示优先级,越小越高。
取值范围为0-1000
4.2常用方法
class FirstPipeline(object):
def __init__(self):
print('~~~~~~~~~~ init ~~~~~~~~~~~~')
def open_spider(self, spider):
print(spider,'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
def process_item(self, item, spider):
return item
def close_spider(self, spider):
print(spider,'========================================')
需求
通过pipeline将爬取的数据存入json文件中