Scrapy,Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。
因为好像这个用的比较多,所以看看用这个框架该怎么写爬虫。其实不难,但是中间出了很多神奇的小问题。
输出不正确、改代码结果不变?
其实是因为反复使用命令
scrapy crawl spider -o 1.json
时候,增加的输出数据不会覆盖,而是继续往后面添加。
request不执行
找了半天不知道为啥,其中一个比较靠谱的是
Request(url,meta={'item':item},callback=self.parse2, dont_filter=True)
dont_filter=True
让allowed_domains
失效了。但是改过了还是不行。
最终结果发现改的文件和运行的文件不一样……
为什么会这样呢?我中间做了一部分实现了初始功能,就重命名了备份,然而执行命令行竟然一直在执行备份文件。。
输出为utf-8格式(保存中文)
更改pipeline文件。
import json
import codecs
class WebPipeline(object):
# def process_item(self, item, spider):
# return item
def __init__(self):
# self.file = open('data.json', 'wb')
self.file = codecs.open(
'scraped_data_utf8.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
def spider_closed(self, spider):
self.file.close()
ITEM_PIPELINES = {
'web.pipelines.WebPipeline': 300,
}
如何在解析函数之间传递值?
一种常见的情况:在parse中给item某些字段提取了值,但是另外一些值需要在parse_item中提取,这时候需要将parse中的item传到parse_item方法中处理,显然无法直接给parse_item设置而外参数。 Request对象接受一个meta参数,一个字典对象,同时Response对象有一个meta属性可以取到相应request传过来的meta。所以解决上述问题可以这样做:
def parse(self, response):
# item = ItemClass()
yield Request(url, meta={'item': item},callback=self.parse_item)
def parse_item(self, response):
item = response.meta['item']
item['field'] = value
yield item
使用ImagesPipeline下载
# setting.py
ITEM_PIPELINES = ['demo.pipelines.MyImagesPipeline'] # ImagePipeline的自定义实现类
IMAGES_STORE = 'D:\\dev\\python\\scrapy\\demo\\img' # 图片存储路径
IMAGES_EXPIRES = 90 # 过期天数
IMAGES_MIN_HEIGHT = 100 # 图片的最小高度
IMAGES_MIN_WIDTH = 100 # 图片的最小宽度
# 图片的尺寸小于IMAGES_MIN_WIDTH*IMAGES_MIN_HEIGHT的图片都会被过滤
ImagePipeline
需要在自定义的ImagePipeline类中重载的方法:get_media_requests(item, info)和item_completed(results, items, info)。
正如工作流程所示,Pipeline将从item中获取图片的URLs并下载它们,所以必须重载get_media_requests,并返回一个Request对象,这些请求对象将被Pipeline处理,当完成下载后,结果将发送到item_completed方法,这些结果为一个二元组的list,每个元祖的包含(success, image_info_or_failure)。 * success: boolean值,true表示成功下载 * image_info_or_error:如果success=true,image_info_or_error词典包含以下键值对。失败则包含一些出错信息。 * url:原始URL * path:本地存储路径 * checksum:校验码。
from scrapy.contrib.pipeline.images import ImagesPipeline
from scrapy.exceptions import DropItem
from scrapy.http import Request
class MyImagesPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
yield Request(image_url)
def item_completed(self, results, item, info):
image_paths = [x['path'] for ok, x in results if ok]
if not image_paths:
raise DropItem("Item contains no images")
item['image_paths'] = image_paths
return item