本文共 6046 字,大约阅读时间需要 20 分钟。
(一)什么是Scrapy呢?Python上优秀的爬虫框架。什么是爬虫?可以看我的,也可以自行谷歌百度。
(二)建议看下的事前准备安装Scrapy。
(三)Selectors根据XPath和CSS表达式从网页中选择数据。XPath和CSS表达式是什么东西,我们不用太过于纠结,只需要知道可以使用它们在网页中选择数据。用法:利用chrome去复制所需数据的位置信息。当然进阶的话可以看
.extract()
,获得是一个列表.extract_first()
,获得是一个字符串.response.css('base::attr(href)')
或.response.xpath('//base/@href')
scrapy startproject tutorial
scrapy genspider -t basic douban douban.com
上面两步会创建如下的目录结构:
简单说下每一个文件的作用,虽然在初识Scrapy已经说过了。
因为Scrapy非常诚实,爬取网页的时候会表明自己是一只爬虫,但是豆瓣不给这些表明身份的爬虫活路。所以我们只能换个身份。
第一步:chrome用快捷键F12打开开发者工具,选择Network一栏,可能需要F5刷新页面:
第二步:在上图红框部分随机选取一个,会出现下图:
我们主要需要的是里面红框的Request Headers的信息。
第三步:在settings.py中修改DEFAULT_REQUEST_HEADERA和USER_AGENT。
ITEM_PIPELINES = { 'tutorial.pipelines.DoubanPipeline': 300,}
用于处理数据 在命令行中输入scrapy shell https://movie.douban.com/chart
这时候会进入scrapy版的ipython,输入view(response)
就可以查看网页。
在items.py中作如下修改
import scrapyclass DoubanItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() link = scrapy.Field() info = scrapy.Field() desc = scrapy.Field()
爬取多个网页前,我们首先得要成功提取一个网页的信息。在spiders/douban.py做如下修改
# -- coding: utf-8 -- import scrapy from scrapy.http import Request from ..items import DoubanItemclass DoubanSpider(scrapy.Spider): name = "douban" allowed_domains = ["douban.com"] start_urls = ( 'https://book.douban.com/tag/%E6%BC%AB%E7%94%BB?start=0&type=T', ) def parse(self, response): item = DoubanItem() for sel in response.css('#subject_list > ul > li > div.info'): item['title']= sel.css('h2 > a::text').extract_first() item['link'] = sel.css('h2 > a::attr(href)').extract_first() item['info'] = sel.css('div.pub::text').extract_first() item['desc'] = sel.css('p::text').extract_first() yield item
大致的爬虫就完成了。用scrapy crawl douban
开始工作。由于scrap构建在python2.7上,所以对中文支持不太好,在命令行中会以unicode编码的方式显示,所以在shell上看到一堆不认识的\xxx也不要太担心。
为了方便之后调用数据,我们需要用pipelines.py将爬取的数据存储在固定的文件中。可以用json等格式储存,也可以存放在数据库中。网页爬取数据往往不太规范,建议使用mongodb(NoSQL)。
import jsonimport codecsImport pymongo #python中用来操作mongodb的库##存储为json格式class DoubanPipeline(object): def __init__(self): self.file = codecs.open('douban_movie.json','wb',encoding='utf-8') def process_item(self, item, spider): line = json.dumps(dict(item)) + '\n' self.file.write(line.decode("unicode_escape")) return itemclass MongoPipeline(object): collection_name = 'douban_cartoon' # mongo的collection相当于sql的table def __init__(self, mongo_uri,mongo_db): self.mongo_uri = mongo_uri self.mongo_db = mongo_db ## 配置mongo @classmethod def from_crawler(cls, crawler): return cls( mongo_uri=crawler.settings.get('MONGO_URI'), #从settings中mongo的uri mongo_db=crawler.settings.get('MONGO_DATABASE','douban') #从settings中获取数据库,默认为douban ) # 在spider工作开始前连接mongodb def open_spider(self, spider): self.client = pymongo.MongoClient(self.mongo_uri) self.db = self.client[self.mongo_db] ## 在spider工作结束后关闭连接 def close_spider(self, spider): self.client.close() ## 在mongodb中插入数据 def process_item(self, item, spider): # for i in item: self.db[self.collection_name].insert(dict(item)) return item
运行后就可以在项目所在目录找到douban_movie.json,mongodb的话需要自己去查询了。
我们需要在这一页获取下一个的链接,然后重新调用parse函数爬取这个链接。
def parse(self, response): ..... ## 获取下一个的链接 href = response.xpath('//*[@id="subject_list"]/div[2]/span[4]/a') url = u'https://book.douban.com'+ href.css('a::attr(href)').extract_first() yield Request(url, callback=self.parse)
我们还可以通过Scrapy提供的CrawlSpider完成多页爬取。CrawlSpider比Spider多了一步即设置Rule,具体可以看我的[Scrapy基础之详解Spider]的CrawlSpider。
为了确保LinkExtractor能提取到正确的链接,我们需要在shell中进行试验。
scrapy shell https://book.douban.com/tag/漫画 from scrapy.linkextractors import LinkExtractor ##导入LinkExtractor item=LinkExtractor(allow='/tag/漫画',restrict_xpaths=('//*[@id="subject_list"]/div[2]/span/a')).extract_links(response) ##需要反复修改
修改后的爬虫如下:
# -- coding: utf-8 -- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from tutorial.items import DoubanItemclass ManhuaSpider(CrawlSpider): name = 'manhua' allowed_domains = ['book.douban.com'] start_urls = ['https://book.douban.com/tag/漫画'] rules = ( Rule(LinkExtractor(allow=r'/tag/漫画', restrict_xpaths=('//*[@id="subject_list"]/div[2]/span/a')), callback='parse_item', follow=True), ) def parse_item(self, response): item = DoubanItem() for sel in response.css('#subject_list > ul > li > div.info'): item['title']= sel.css('h2 > a::text').extract_first() item['link'] = sel.css('h2 > a::attr(href)').extract_first() item['info'] = sel.css('div.pub::text').extract_first() item['desc'] = sel.css('p::text').extract_first() yield item
运行结果和多页逻辑(一)的一致。
进一步,你可以看,在这个的基础上增加图片下载功能。
如果怕被ban,可以看本文参考了的,
,以及最重要的官方文档。写在最后:
网络上有那么多的Scrapy的教程,为啥我还要写一个呢?因为我觉得真正学会用自己语言去表达一门技术的时候,才算入门了。 还有写出来的东西才能让别人发现自己的不足,希望各位大大批评指正。 我的源代码托管在上,有需要的话可以去看转载地址:http://hexsa.baihongyu.com/