本篇是python实例编写:糗事百科 ,目的是介绍最基础的scrapy框架的写法和运行过程。
功能描述 目标:获取 糗事百科 所所有信息的作者,url和内容,需要爬取多个页面,/page/1~/page/13。
输出:保存到文件中(json格式)
编写步骤 编写spider 处理链接爬取和页面解析,编写pipelines 处理信息存储
步骤1:建立工程和Spider模板 1 2 3 \>scrapy startproject qsbk \>cd BaiduStocks \>scrapy genspider qsbk_spider qiushibaike.com
步骤2:编写Spider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import scrapyfrom urllib.parse import urljoinfrom qsbk.items import QsbkItemclass QsbkSpiderSpider (scrapy.Spider) : name = 'qsbk_spider' allowed_domains = ['qiushibaike.com' ] start_urls = ['http://qiushibaike.com/text/page/1/' ] def parse (self, response) : duanziDivs = response.xpath("//div[@class='col1 old-style-col1']/div" ) i = 0 for div in duanziDivs: author = div.xpath(".//h2/text()" ).get().strip() href = div.xpath('./a/@href' ).get() url = urljoin(self.start_urls[0 ],href) content = div.xpath(".//div[@class='content']//text()" ).extract() content = '' .join(content).strip() item = QsbkItem(author=author, content=content) i += 1 print('生成器调用:' ,i) yield item
测试该函数可以注释最后的生成器步骤,取消注释前面的打印函数。
对setting.py的设置 1 2 3 4 5 user-agent = 'Mozilla/5.0' ROBOTSTXT_OBEY = False LOG_LEVEL = "WARN"
步骤3:编写ITEM Pipelines 注意该文件中,类里的三个方法: 打开、运行、关闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from itemadapter import ItemAdapterimport jsonclass QsbkPipeline : def __init__ (self) : self.fp = open('duanzi.json' , 'w' , encoding='utf-8' ) def open_spider (self, spider) : print('--------> 爬虫开始了... <---------' ) def process_item (self, item, spider) : item_json = json.dumps(dict(item),ensure_ascii=False ) self.fp.write(item_json + '\n' ) print('pipelines调用' ) return item def close_spider (self, spider) : self.fp.close() print('--------> 爬虫结束了... <---------' )
items.py编写 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import scrapyclass QsbkItem (scrapy.Item) : author = scrapy.Field() content = scrapy.Field()
注意 若要使用ITEM Pipelines,需要在setting.py中开启。
1 2 3 4 5 ITEM_PIPELINES = { 'qsbk.pipelines.QsbkPipeline' : 300 , }
优化数据存储的方式 方法一:使用JsonItemExporter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from scrapy.exporters import JsonItemExporter class QsbkPipeline : def __init__ (self) : self.fp = open('duanzi.json' , 'wb' ) self.exporter = JsonItemExporter(self.fp, ensure_ascii = False , encoding = 'utf-8' ) self.exporter.start_exporting() def open_spider (self, spider) : print('--------> 爬虫开始了... <---------' ) def process_item (self, item, spider) : self.exporter.export_item(item) print('pipelines调用' ) return item def close_spider (self, spider) : self.exporter.finish_exporting() self.fp.close() print('--------> 爬虫结束了... <---------' )
缺点:所有item存在一个列表中,一起写入文件,较消耗内存。
需要 开始 和 结束 。
但整体满足格式要求,一个大列表,每个元素是一个item的json格式数据。
方法二:使用JsonLinesItemExporter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from scrapy.exporters import JsonLinesItemExporterclass QsbkPipeline : def __init__ (self) : self.fp = open('duanzi.json' , 'wb' ) self.exporter = JsonLinesItemExporter(self.fp, ensure_ascii = False , encoding = 'utf-8' ) def open_spider (self, spider) : print('--------> 爬虫开始了... <---------' ) def process_item (self, item, spider) : self.exporter.export_item(item) print('pipelines调用' ) return item def close_spider (self, spider) : self.fp.close() print('--------> 爬虫结束了... <---------' )
此方法把每个item作为一个单独的字典类型存入文件。
不需要 开始 和 结束 。
缺点:每行数据为一个字典(json格式),但整个文件并不满足json格式。
抓取多个页面 主要是最后几行代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import scrapyfrom urllib.parse import urljoinfrom qsbk.items import QsbkItemclass QsbkSpiderSpider (scrapy.Spider) : name = 'qsbk_spider' allowed_domains = ['qiushibaike.com' ] start_urls = ['http://qiushibaike.com/text/page/1/' ] def parse (self, response) : duanziDivs = response.xpath("//div[@class='col1 old-style-col1']/div" ) i = 0 for div in duanziDivs: author = div.xpath(".//h2/text()" ).get().strip() href = div.xpath('./a/@href' ).get() url = urljoin(self.start_urls[0 ],href) content = div.xpath(".//div[@class='content']//text()" ).extract() content = '' .join(content).strip() item = QsbkItem(author=author, content=content) i += 1 print('生成器调用:' ,i) yield item next_path = response.xpath("//ul[@class='pagination']/li[last()]/a/@href" ).get() if not next_path: return else : next_url = urljoin(self.start_urls[0 ], next_path) yield scrapy.Request(next_url, callback=self.parse)