Scrapy框架css
Scrapy是用Python实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架。能够应用在包括数据挖掘、信息处理或存储历史数据等一系列的程序中。html
Scrapy使用Twisted基于事件的高效异步网络框架来处理网络通讯,能够加快下载速度,不用本身去实现异步框架,而且包含了各类中间件接口,能够灵活的完成各类需求。python
Scrapy架构正则表达式
Scrapy Engineshell
引擎,负责控制数据流在系统中全部组件中流动,并在相应动做发生时触发事件。此组件至关于爬虫的“大脑”,是整个爬虫的调度中心数据库
调度器(Scheduler)编程
调度器接收从引擎发送过来的request,并将他们入队,以便以后引擎请求他们时提供给引擎json
初始的爬取URL和后续在页面中获取的待爬取的URL将放入调度器中,等待爬取。同时调度器会自动去除重复的URL(若是特定的URL不须要去重也能够经过设置实现,如post请求的URL)windows
下载器(Downloader)api
下载器负责获取页面数据并提供给引擎,然后提供给spider
Spiders爬虫
Spider是编写的类,做用以下:
Scrapy用户编写用于分析response并提取item(即获取到的item)
额外跟进的URL,将额外跟进的URL提交给引擎,加入到Scheduler调度器中。将每一个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)
1.引擎打开一个网站(open a domain),找处处理该网站的Spider并向该spider请求第一个(批)要爬取的URL(s)
2.引擎从Spider中获取到第一个要爬取的URL并加入到调度器(Scheduler)做为请求以备调度
3.引擎向调度器请求下一个要爬取的URL
4.调度器返回下一个要爬取的URL给引擎,引擎将URL经过下载中间件并转发给下载器(Downloader)
5.一旦页面下载完毕,下载器生成一个该页面的Response,并将其经过下载中间件发送给引 擎
6.引擎从下载器中接收到Response,而后经过Spider中间件发送给Spider处理
7.Spider处理Response并返回提取到的Item及(跟进的)新的Request给引擎
8.引擎将Spider返回的Item交给Item Pipeline,将Spider返回的Request交给调度器
9.(从第二步)重复执行,直到调度器中没有待处理的request,引擎关闭
注意:
只有当调度器中没有任何request了,整个程序才会中止执行。若是有下载失败的URL,会从新下载
安装scrapy
安装wheel支持
$ pip install wheel
安装scrapy框架
$ pip install scrapy
window下,为了不windows编译安装twisted依赖,安装下面的二进制包
$ pip install Twisted-18.4.0-cp35-cp35m-win_amd64.whl
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,Python Extension Packages for Windowspython3.5 下载 Twisted-18.4.0-cp35-cp35m-win_amd64.whlpython3.6 下载 Twisted-18.4.0-cp36-cp36m-win_amd64.whl安装twisted$ pip install Twisted-18.4.0-cp35-cp35m-win_amd64.whl以后在安装scrapy就没有什么问题了
安装好,使用scrapy命令看看
1.> scrapy 2.Scrapy 1.5.0 - no active project 3. 4.Usage: 5. scrapy <command> [options] [args] 6. 7.Available commands: 8.bench Run quick benchmark test 9.check Check spider contracts 10.crawl Run a spider 11.edit Edit spider 12.fetch Fetch a URL using the Scrapy downloader 13.genspider Generate new spider using pre-defined templates 14.list List available spiders 15.parse Parse URL (using its spider) and print the results 16.runspider Run a self-contained spider (without creating a project) 17.settings Get settings values 18.shell Interactive scraping console 19.startproject Create new project 20.version Print Scrapy version 21.view Open URL in browser, as seen by Scrapy
Scrapy开发
项目编写流程
1.建立项目
使用 scrapy startproject proname 建立一个scrapy项目
scrapy startproject <project_name> [project_dir]
2.编写item
在items.py中编写Item类,明确从response中提取的item
3.编写爬虫
编写spiders/proname_spider.py,即爬取网站的spider并提取出item
4.编写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
BOT_NAME 爬虫名
ROBOTSTXT_OBEY = True 是否听从robots协议
USER_AGENT = '' 指定爬取时使用
CONCURRENT_REQEUST = 16 默认16个并行
DOWNLOAD_DELAY = 3 下载延时,通常要设置,不宜过快发起连续请求
COOKIES_ENABLED = False 缺省是启用,通常须要登陆时才须要开启cookie
SPIDER_MIDDLEWARES 爬虫中间件
DOWNLOADER_MIDDLEWARES 下载中间件
'firstscrapy.pipelines.FirstscrapyPipeline': 300item交给哪个管道处理,300 越小优先
级越高
ITEM_PIPELINES 管道配置
'first.middlewares.FirstDownloaderMiddleware': 543543 越小优先级越高
spiders目录
__init__.py 必须有,能够在这里写爬虫类,也能够写爬虫子模块
1.# first/settings.py参考2.BOT_NAME = 'first'3.SPIDER_MODULES = ['first.spiders']4.NEWSPIDER_MODULE = 'first.spiders'5.6.USER_AGENT = "Mozilla/5.0 (Windows NT 6.1)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"7.ROBOTSTXT_OBEY = False8.9.DOWNLOAD_DELAY = 310.11.# Disable cookies (enabled by default)12.COOKIES_ENABLED = False
注意必定要更改User-Agent,不然访问https://book.douban.com/会返回403
2 编写Item
1.在items.py中编写 2.import scrapy 3.class BookItem(scrapy.Item): 4.title = scrapy.Field() # 书名 5.rate = scrapy.Field() # 评分
3 编写爬虫
为爬取豆瓣书评编写爬虫类,在spiders目录下:
编写的爬虫类须要继承自scrapy.Spider,在这个类中定义爬虫名、爬取范围、其实地址等
在scrapy.Spider中parse方法未实现,因此子类应该实现parse方法。该方法传入response对象
1.# scrapy源码中 2.class Spider(): 3.def parse(self, response): # 解析返回的内容 4.raise NotImplementedError
爬取读书频道,tag为“编程”的书名和评分:
https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=20&type=T
使用模板建立spider, $ scrapy genspider -t basic book https://www.douban.com/
1.import scrapy 2. 3.class BookSpider(scrapy.Spider): # BookSpider 4.name = 'doubanbook' # 爬虫名,可修改,重要 5.allowed_domains = ['豆瓣'] # 爬虫爬取范围 6.url = '豆瓣图书标签: 编程' 7.start_urls = [url] # 起始URL 8. 9.# 下载器获取了WEB Server的response就好了,parse就是解析响应的内容 10.def parse(self, response): 11. print(type(response), '~~~~~~~~~') #scrapy.http.response.html.HtmlResponse 12.print(response) 13.print('-' * 30)
使用crawl爬取子命令
1.$ scrapy list 2.$ scrapy crawl -h 3.scrapy crawl [options] <spider> 4. 5.指定爬虫名称开始爬取 6.$ scrapy crawl doubanbook 7. 8.能够不打印日志 9.$ scrapy crawl doubanbook --nolog
若是在windows下运行发生twisted的异常 ModuleNotFoundError: No module named 'win32api' ,请安装 $ pip install pywin32。
response是服务器端HTTP响应,它是scrapy.http.response.html.HtmlResponse类。
由此,修改代码以下
1.import scrapy 2.from scrapy.http.response.html import HtmlResponse 3. 4.class BookSpider(scrapy.Spider): # BookSpider 5. name = 'doubanbook' # 爬虫名 6. allowed_domains = ['豆瓣'] # 爬虫爬取范围 7. url = '豆瓣图书标签: 编程' 8.start_urls = [url] # 起始URL 9. 10. # 下载器获取了WEB Server的response就好了,parse就是解析响应的内容 11.def parse(self, response:HtmlResponse): 12. print(type(response)) #scrapy.http.response.html.HtmlResponse 13. print('-'*30) 14. print(type(response.text), type(response.body)) 15.print('-'*30) 16.print(response.encoding) 17.with open('o:/testbook.html', 'w', encoding='utf-8') as f: 18. try: 19. f.write(response.text) 20. f.flush() 21. except Exception as e: 22.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
1.import scrapy 2.from scrapy.http.response.html import HtmlResponse 3. 4.response = HtmlResponse('file:///O:/testbook.html', encoding='utf-8') # 构造对象 5. 6.with open('o:/testbook.html', encoding='utf8') as f: 7.response._set_body(f.read()) # 填充数据 8.#print(response.text) 9. 1O.# 获取全部标题及评分 11.# xpath解析 12.subjects = response.xpath('//li[@class="subject-item"]') 13.for subject in subjects: 14.title = subject.xpath('.//h2/a/text()').extract() # list 15.print(title[0].strip()) 16. 17.rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract() 18.print(rate[0].strip()) 19. 2O.print('-'*30) 21.# css解析 22.subjects = response.css('li.subject-item') 23.for subject in subjects: 24.title = subject.css('h2 a::text').extract() 25.print(title[0].strip()) 26. 27.rate = subject.css('span.rating_nums::text').extract() 28.print(rate[0].strip()) 29.print('-'*30) 30. 31. # xpath和css混合使用、正则表达式匹配 32.subjects = response.css('li.subject-item') 33.for subject in subjects: 34.# 提取连接 35.href =subject.xpath('.//h2').css('a::attr(href)').extract() 36.print(href[0]) 37. 38. # 使用正则表达式 39.id = subject.xpath('.//h2/a/@href').re(r'\d*99\d*') 40.if id: 41.print(id[0]) 42. 43.# 要求显示9分以上数据 44.rate = subject.xpath('.//span[@class="rating_nums"]/text()').re(r'^9.*') 45.# rate = subject.css('span.rating_nums::text').re(r'^9\..*') 46.if rate: 47.print(rate)
3.2 item封装数据
1.# spiders/bookspider.py 2.import scrapy 3.from scrapy.http.response.html import HtmlResponse 4.from ..items import BookItem 5. 6.class BookSpider(scrapy.Spider): # BookSpider 7.name = 'doubanbook' # 爬虫名 8.allowed_domains = ['豆瓣'] # 爬虫爬取范围 9.url = '豆瓣图书标签: 编程' 10.start_urls = [url] # 起始URL 11. 12. # 下载器获取了WEB Server的response就好了,parse就是解析响应的内容 13.def parse(self, response:HtmlResponse): 14.items = [] 15.# xpath解析 16.subjects = response.xpath('//li[@class="subject-item"]') 17.for subject in subjects: 18.title = subject.xpath('.//h2/a/text()').extract() 19.rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract_first() 20.item = BookItem() 21.item['title'] = title[0].strip() 22.item['rate'] = rate.strip() 23. items.append(item) 24. 25. print(items) 26. 27.return items # 必定要return,不然保存不下来 28. 29.# 使用命令保存return的数据 30.# scrapy crawl -h 31.# --output=FILE, -o FILE dump scraped items into FILE (use - for stdout) 32.# 文件扩展名支持'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle' 33.# scrapy crawl doubanbook -o dbbooks.json
获得下图数
注意上图的数据已是unicode字符,汉字的unicode表达。
4 pipeline处理
将bookspider.py中BookSpider改为生成器,只须要把 return items 改形成 yield item ,即由产生一个列表变成yield一个个item
脚手架帮咱们建立了一个pipelines.py文件和一个类
4.1 开启pipeline
1.# Configure item pipelines 2.# See Item Pipeline - Scrapy 1.8.0 documentation 3.ITEM_PIPELINES = { 4.'first.pipelines.FirstPipeline': 300, 5.}
整数300表示优先级,越小越高。
取值范围为0-1000
4.2经常使用方法
1.class FirstPipeline(object): 2.def __init__(self): # 全局设置 3. print('~~~~~~~~~~ init ~~~~~~~~~~~~') 4. 5.def open_spider(self, spider): # 当某spider开启时调用 6. print(spider,'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') 7. 8.def process_item(self, item, spider): 9. # item 获取的item;spider 获取该item的spider 10.return item 11. 12.def close_spider(self, spider): # 当某spider关闭时调用 13.print(spider,'========================================') 14.
需求
经过pipeline将爬取的数据存入json文件中
1.# spider/bookspider.py 2.import scrapy 3.from scrapy.http.response.html import HtmlResponse 4.from ..items import BookItem 5. 6.class BookSpider(scrapy.Spider): # BookSpider 7. name = 'doubanbook' # 爬虫名 8. allowed_domains = ['豆瓣'] # 爬虫爬取范围 9.url = '豆瓣图书标签: 编程' 10. start_urls = [url] # 起始URL 11. 12.# spider上自定义配置信息 13.custom_settings = { 14. 'filename' : 'o:/books.json' 15. } 16.# 下载器获取了WEB Server的response就好了,parse就是解析响应的内容 17.def parse(self, response:HtmlResponse): 18. #items = [] 19.# xpath解析 20.subjects = response.xpath('//li[@class="subject-item"]') 21.for subject in subjects: 22.title = subject.xpath('.//h2/a/text()').extract() 23.rate =subject.xpath('.//span[@class="rating_nums"]/text()').extract_first() 24.item = BookItem() 25.item['title'] = title[0].strip() 26.item['rate'] = rate.strip() 27.#items.append(item) 28. 29.yield item 30.#return items 31. 32.# pipelines.py 33.import simplejson as json 34. 35.class FirstPipeline(object): 36. def __init__(self): # 全局设置 37. print('~~~~~~~~~~ init ~~~~~~~~~~~~') 38. 39.def open_spider(self, spider): # 当某spider开启时调用 40. print('{} ~~~~~~~~~~~~~~~~~~~~'.format(spider)) 41. print(spider.settings.get('filename')) 42.self.file = open(spider.settings['filename'], 'w', encoding='utf-8') 43.self.file.write('[\n') 44. 45.def process_item(self, item, spider): 46.# item 获取的item;spider 获取该item的spider 47.self.file.write(json.dumps(dict(item)) + ',\n') 48.return item 49. 50.def close_spider(self, spider): # 当某spider关闭时调用 51.self.file.write(']') 52.self.file.close() 53.print('{} ======================='.format(spider)) 54.print('-'*30)
5 url提取
若是要爬取下一页内容,能够本身分析每一页的页码变化,也能够经过提取分页栏的连接
1.# spider/bookspider.py 2.import scrapy 3.from scrapy.http.response.html import HtmlResponse 4.from ..items import BookItem 5. 6.class BookSpider(scrapy.Spider): # BookSpider 7.name = 'doubanbook' # 爬虫名 8.allowed_domains = ['豆瓣'] # 爬虫爬取范围 9.url = '豆瓣图书标签: 编程' 10.start_urls = [url] # 起始URL 11. 12.# spider上自定义配置信息 13.custom_settings = { 14.'filename' : 'o:/books.json' 15.} 16. 17.# 下载器获取了WEB Server的response就好了,parse就是解析响应的内容 18.def parse(self, response:HtmlResponse): 19.#items = [] 20.# xpath解析 21.# 获取下一页,只是测试,因此使用re来控制页码 22.print('-' * 30) 23.urls = response.xpath('//div[@class="paginator"]/span[@class="next"]/a/@href').re( 24. r'.*start=[24]\d[^\d].*') 25.print(urls) 26.print('-' * 30) 27.yield from (scrapy.Request(response.urljoin(url)) for url in urls) 28.print('++++++++++++++++++++++++') 29. 30.subjects = response.xpath('//li[@class="subject-item"]') 31.for subject in subjects: 32.# 解决图书副标题拼接 33.title = "".join(map(lambda x:x.strip(), subject.xpath('.//h2/a//text()').extract())) 34.rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract_first() 35.#print(rate) # 有的没有评分,要注意可能返回None 36. 37.item = BookItem() 38.item['title'] = title 39.item['rate'] = rate 40.#items.append(item) 41.yield item 42. 43.#return items