网页爬虫抓取百度图片(Python非常有名的爬虫框架使用教程参考:初窥Scrapy)
优采云 发布时间: 2022-04-08 04:14网页爬虫抓取百度图片(Python非常有名的爬虫框架使用教程参考:初窥Scrapy)
博客地址:Scrappy入门:*敏*感*词*图片爬虫
Scrapy 是一个非常知名的 Python 爬虫框架。框架本身对爬虫性能做了很多优化:多线程、集成xpath和image-specific pipelines等,开发者只需要关注功能需求即可。
基本 Scrapy 教程参考:初看 Scrapy 和 Scrapy 入门教程。
学习一个技术或者一个框架最好的方法当然是用它来做一些小项目。上手的第一步是选择*敏*感*词*图片爬虫,因为它够简单,也比较实用。
因为这次涉及到图片的下载,而Scrapy本身为此提供了专门的图片管道,所以果断使用Scrapy的图片管道来帮助完成。Scrapy中管道的定义如下:
在Spider中采集到Item后,会传递给Item Pipeline,一些组件会按照一定的顺序执行对Item的处理。
每个项目管道组件(有时称为“项目管道”)都是一个实现简单方法的 Python 类。他们接收项目并对其执行一些操作,并决定该项目是继续通过管道,还是被丢弃而不进行进一步处理。
管道的典型应用场景如下:
清理 HTML 数据
验证抓取的数据(检查项目是否收录某些字段)
检查重复项(并丢弃)
将爬取结果保存到数据库
使用 Scrappy 图像管道的教程参考:下载项目图像。
使用 Scrapy 最重要的是编写一个特定的蜘蛛类。本文指定的蜘蛛类是BaiduTieBaSpider。我们来看看它的定义:
import scrapy
import requests
import os
from tutorial.items import TutorialItem
class BaiduTieBaSpider(scrapy.spiders.Spider):
name = 'baidutieba'
start_urls = ['http://tieba.baidu.com/p/2235516502?see_lz=1&pn=%d' % i for i in range(1, 38)]
image_names = {}
def parse(self, response):
item = TutorialItem()
item['image_urls'] = response.xpath("//img[@class='BDE_Image']/@src").extract()
for index, value in enumerate(item['image_urls']):
number = self.start_urls.index(response.url) * len(item['image_urls']) + index
self.image_names[value] = 'full/d.jpg' % number
yield item
以下是 Scrappy 所做的两件事:
然后首先是start_urls的构建,这里是*敏*感*词*中的url规则,其中see_lz=1表示只看楼主,pn=1表示第一页,根据这些规则,一个url数组获得。然后观察单个页面的html源码,发现每层发布的图片对应的img标签的class是BDE_Image,这样就可以得到xpath的表达式:xpath("//img[@class ='BDE_Image'] /@src"),提取楼内所有图片的src,赋值给item对象的image_urls字段。当蜘蛛返回时,item会进入图片管道进行处理(即Scrapy会自动为你下载图片)。
对应item类的编写和setting.py文件的修改在上面的教程中有详细介绍。
到这里下载图片的基本功能已经完成了,但是有个问题:想按顺序保存图片怎么办?
这个问题的关键在于scrapy多线程爬取页面,即start_urls中地址的取是异步请求,而每张图片的url也是item返回图片管道后的异步请求,所以是不可能的。返回每个图像的顺序是有保证的。
那么如何解决这个问题呢?尝试了几种方法,得到了一个比较理想的解决方案:做一个字典,key是图片地址,value是对应的数字。所以代码中有image_names和number = self.start_urls.index(response.url) * len(item['image_urls']) + index,然后自定义image pipeline。上面给出了定制方法。教程链接,本文需要自定义的是重写file_path函数,代码如下:
import scrapy
from scrapy.contrib.pipeline.images import ImagesPipeline
from scrapy.exceptions import DropItem
from tutorial.spiders.BaiduTieBa_spider import BaiduTieBaSpider
class MyImagesPipeline(ImagesPipeline):
def file_path(self, request, response=None, info=None):
image_name = BaiduTieBaSpider.image_names[request.url]
return image_name
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
yield scrapy.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
file_path 函数是返回每个图像保存的路径。当我们有了完整的字典后,我们只需要根据请求的 URL 获取对应的数字即可。
这种方法显然更占内存,因为如果图片很多,就会有很多字典条目需要维护,但是从折腾过的几个解决方案(比如手动下载图片不用管道)到看,它效果最好,而且付出的代价是可以接受的。
第一个开始使用 Scrappy 的小演示写在这里。