Python初识——Scrapy抓取二次元小姐姐图片

2023-12-23 03:59

本文主要是介绍Python初识——Scrapy抓取二次元小姐姐图片,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

写在前面

最近在学习Python这门语言,禀着实践是最好的学习方法的原则,上来就迫不及待的学习了Scrapy框架,并结合网上的例子进行了实验,不得不说感觉到了Python和Scrapy的强大之处,同时也感觉算是站在Python的门外,窥得厅堂里面的东西,还是有些小激动的。那么本帖算是对往日工作的一个总结,也希望自己能够把总结这个习惯坚持下去,一方面帮助自己,一方面帮助他人。


参考链接

在此还是很感谢网上许许多多大神们的帖子和经验,这里先列出学习过程中参考的链接,希望可以帮助更多的人。

Scrapy tutorial:https://doc.scrapy.org/en/latest/intro/tutorial.html  很有用的官方教程,建议初学者看一下

知乎参考程序:https://www.zhihu.com/question/27621722/answer/269085034 个人感觉还是很有意思的,程序理解起来也不难

Python3语句变为Python2:http://blog.csdn.net/whatday/article/details/54710403 这个博客讲了urllib库在Python2和Python3中的区别,想用Python2的话就把里面对应的语句改一下就可以了(亲测好用)。


Scrapy浅析

首先奉上整个框架的框架图:

可以看到,Scrapy主要包含一下几个方面的组件:

Scrapy Engine(引擎):主要用来处理整个系统的数据流,触发事务。

Scheduler(调度器):主要接收引擎发过来的请求,途中的数据流表示爬虫(spider)将请求给引擎,引擎再给调度器。这个调度器可以看成是一个带有去除重复URL的队列,由他决定下一时刻要去抓取哪个网页,同时去除重复的网址。

Downloader(下载器):由图可以看到,Downloader用于接收Scheduler的请求,之后就开始对请求的网页进行下载,下载后的网页将储存在Response中给spider。

Spider(爬虫):这部分就是我们需要重点对待的部分了,也是程序的主题部分。当spider收到回复之后,就可以使用Scrapy的Selector进行感兴趣内容的抓取,同时还可以生成下次要抓取的页面通过请求的方式给引擎。

ItemPipeline(项目管道):Item是整个抓取过程的实体,也就是我们所需要的资源,个人认为一般就是我们需要的链接,例如图片,视频等的URL;而Pipeline接到这样的实体之后,就可以对实体中的内容进行处理,例如下载图片和音频等工作,这部分也是我们需要编程的部分。

Middlewares(中间件):各种中间件,从图中来看应该是用于数据的流向,具体的功能还没有研究到。


再来大致讲一下整个框架的运行流程:

1. 爬虫将URL以请求(Request)的形式发送给引擎,引擎给予调度器。

2. 引擎从调度器中取出将要抓取的URL(更确切的说是请求),发送给下载器,下载器对该URL进行下载。

3. 下载器将下载的内容以回复(Response)的方式给引擎,引擎转送给爬虫。

4. 爬虫对回复进行解析,将解析出的实体(Item)通过引擎给予管道(Pipeline),将解析出的URL(下一次要抓取的网页)通过引擎给调度器。

5. 管道接收到实体之后对其中的资源进行下载。


Scrapy抓取http://www.acg.fi/

作为一个来自二次元的程序员,这个网站着实让人眼前一亮,毕竟之前只知道B站:D

废话不多说,直接说如何进行该网站的抓取。

首先用浏览器打开要抓取的网页acg.fi,随后我们查看他的源代码,发现这个网站的源代码真心的乱啊,一点儿都不归正:|不过没有关系,我们可以用检查的模式进行代码的浏览,很快就能找到我们想要解析的地方如下图所示

可以看到我们主要要解析的标签为<li>下的<a>标签,通过总结我们发现<li>标签下的id属性里都有“menu-item”这样的字段,很方便的就可以区分与其他的<li>标签,这时候我们就可以编写代码了,这里主要用xpath的方法,程序也很简单,rlt = response.xpath("//li[contains(@id,'menu-item')]//a/@href").extract()就可以提取到上面的七个板块的URL了,但是请注意并不是所有的版块都是有图的,比如上面的信息页和大事记等,因此还要进行列表(list)的切片操作,随后把URL封装成Request的形式发送出去就好了。这里还是强烈建议新手多看看scrapy的tutorial里面关于Selector的讲解,很有受用(链接:https://doc.scrapy.org/en/latest/topics/selectors.html)

如果这个地方没有了问题,那么解析网页基本上就没啥大问题了,可以看到后面的操作基本上都是这么找标签,然后提取。爬虫部分的代码也就可以贴上来了:D


spider代码

#!/usr/bin/env python
# coding:utf-8from acg.items import AcgItem;
from acg import settings;
import os;
import scrapy;
import urllib.request;
import urllib.parse;class acgspiders(scrapy.Spider):''' 爬取一个网站 '''name = 'images';start_urls = ["http://www.acg.fi",];count = 0;page = 1;''' 三级爬取 '''''' 该部分主要获取要取得的内容 '''def parse3(self, response):# 判断已经获取了多少张图片了# 如果获取了够3000张图片了,就返回if self.count >= 3000:return None;# 先获取itemitem = response.meta["item"];# 抓取图片的urlimage_url = response.xpath("//article[@class='article-content']//img/@src").extract();# 打印一共有多少个图片urlprint("一共找到图片%d张" % len(image_url));# 进行图片的抓取for url in image_url:url = urllib.request.quote(url,safe='/:?=.!');if "jpg" in url:self.count += 1;item["img_url"] = url;item["img_name"] = "img"+str(self.count);yield item;if "png" in url:self.count += 1;item["img_url"] = url;item["img_name"] = "img"+str(self.count);yield item;''' 二级爬取 '''''' 该爬取主要是将各栏目中的页面的url给爬取出来 '''def parse2(self, response):# 先获取itemitem = response.meta["item"];# 爬取该栏目下的分页情况pages_url = response.xpath("//div[@class='fenye']//a/@href").extract();# 这个num主要是为了产生各下级页面的urlnum = 1;# 先判断该栏目是否只有一个页面if len(pages_url)==0:# 如果是的话,把该栏目该页的url作为iterator的内容给parse3page = response.xpath("//h1[@class='article-title']//a/@href").extract_first();yield scrapy.Request(page, meta={"item":item}, callback=self.parse3);else:# 如果该栏目下有多页的话,则把各个页面的url取出来# 这里一定要进行切片操作,因为每次进来的都是第一页,而下面总会有一个# 按钮名曰“下一页”,这里就是要舍弃这个下一页for page in pages_url[:len(pages_url)-1]:# 对该页面进行请求yield scrapy.Request(page, meta={"item":item}, callback=self.parse3);# 下级页面自加num+=1;# 打印当前自己爬取了多少界面print(">>>  [parse2] -> 该栏目一共有%d个分页" % num);''' 一级爬取 '''''' 该爬取主要找到当前界面下有多少个栏目(二级界面) '''def parse1(self, response):# 首先获取item的值item = response.meta["item"];# 爬取该网站下的二级界面pages_url = response.xpath("//div[@class='card-item']//h3//a/@href").extract();# 打印出来找到了多少个二级页面print(">>>  [parse1] -> %s栏目的第%d页一共有%d个卡片" % (item["dir_name"],int(item["page_num"]),len(pages_url)));# 生成generatorfor url in pages_url:yield scrapy.Request(url,meta={"item":item}, callback=self.parse2);# 爬到网页数自加,由于网页数量可能比较多,因此不希望无限制的抓取下去if item["page_num"] < 20:item["page_num"]=item["page_num"]+1;print(">>>  [parse1] -> item's page_num is : %d" % item["page_num"]);# 找寻下个一级页面next_url = item["lm_url"]+"/page/"+str(item["page_num"]);# 这里只生成一个generatoryield scrapy.Request(next_url,meta={"item":item},callback=self.parse1);''' 初级爬取 '''''' 该函数主要找到该网站下的各个分栏,之后给parse1 '''def parse(self, response):# 抓取分栏# 要拿到名称和链接lm_name = response.xpath("//li[contains(@id,'menu-item')]//a/text()").extract();lm_url = response.xpath("//li[contains(@id,'menu-item')]//a/@href").extract();# 去除首页和后面的分栏for idx in range(1,7):# 建立文件夹# 先确定要存储的位置path = settings.SAVE_PATH+lm_name[idx];# 判断是否有该位置的文件夹,没有则重新建立if not os.path.exists(path):os.mkdir(path);# 打印栏目的名称和urlprint(">>>  [parse] -> 栏目名称 : %s  url : %s" % (lm_name[idx],lm_url[idx]));# 产生一个itemitem = AcgItem();item["dir_name"] = str(lm_name[idx]);item["lm_url"] = str(lm_url[idx]);item["page_num"] = 1;# 这里预留图像的url和img_name的值# 生成generatoryield scrapy.Request(lm_url[idx], meta={"item":item}, callback=self.parse1);
这里程序也比较简单,也带有我个人认为比较清楚的注释,我的建议是最好亲手撸一遍,过程中不懂的就baidu, bing, google,一般都能找到满意的回答。

这里也着重说一下具有好感的Item,开始在接触这个框架的时候就在想如何在请求与请求之间传递参数,例如本例中的下载路径文件夹,这显然是要在不停的解析下传递下去的,看到Item我就放心了,Scrapy确实为我们想好了策略,而且这个Item还是字典的形式,各种取值赋值都很简单,这里主要还是提醒说在传递的时候,主要通过scrapy.Request中的meta变量进行传递,而且程序中也可以清晰的看到,传递的参数可以是一个,也可以是很多个的!只要把键值区分开就行。最后,一旦觉得实体可以给Pipeline了,直接yield一下该Item就行,太方便了。


Item代码

# -*- coding: utf-8 -*-# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.htmlimport scrapyclass AcgItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()# 文件夹的名称dir_name = scrapy.Field(serializer=str);lm_url = scrapy.Field(serializer=str);page_num = scrapy.Field(serializer=int);img_url = scrapy.Field(serializer=str);img_name = scrapy.Field(serializer=str);

这里Scrapy的Field有一个很好的属性就是serializer,后面的配置可以限制这个变量的类型。


Pipeline代码

# -*- coding: utf-8 -*-# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
from acg.items import AcgItem;
from acg import settings;
from http.client import IncompleteRead;import urllib.error;
import urllib.request;
import urllib.parse;
import numpy as np;
import cv2;
import os;class AcgPipeline(object):''' 初始化函数 '''def __init__(self):self.img_seen = set();''' 执行函数 '''def process_item(self, item, spider):# 判断是否见过这个image_nameif item["img_name"] in self.img_seen:# 如果是的话打印出信息print(">>>  [pipeline] -> old url is used!");# 并返回return item;else:# 把图片给resize一下,防止过大maxSize = 512;# 获取urlurl = item["img_url"];# 模仿浏览器进行访问req = urllib.request.Request(url=url);req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1");# req.add_header("GET",url);# req.add_header("Host", "img.gov.com.de");req.add_header("Referer", url);# 数据的获取try:# 获取该数据res = urllib.request.urlopen(req, timeout=100).read();# 转化为numpy类型的数据image = np.asarray(bytearray(res), dtype="uint8");# 转化为opencv的数据image = cv2.imdecode(image, cv2.IMREAD_COLOR);# reszieimgResize = cv2.resize(image, dsize=(maxSize, maxSize), interpolation=cv2.INTER_CUBIC);# 存储# 先确定要存储的位置path = settings.SAVE_PATH+item["dir_name"];# 判断是否有该位置的文件夹,没有则重新建立if not os.path.exists(settings.SAVE_PATH):os.makedirs(path);# 生成图片保存的路径img_path = path + "/" + item["img_name"] + ".jpg";# 保存图片cv2.imwrite(img_path, imgResize);# 打印保存信息print(">>>  [pipeline] -> image saves in %s" % img_path);except urllib.error.HTTPError as e:print(">>>  [pipeline] -> HTTPError occur, it's code is %s" % str(e.code));except (IncompleteRead) as e:print(">>>  [pipeline] -> IncompleteRead occur, it's code is %s" % str(e.code));except urllib.error.URLError as e:print(">>>  [pipeline] -> URLError occur, it's code is %s" % str(e.code));# 别忘了返回itemreturn item;
这部分代码也没有特别多的地方需要讲,硬说的话主要是在urllib的urlopen时尽量加上timeout的设置。

PS:如果要用Pipeline进行资源下载,一定要在setting里面把关于Pipeline的注解删掉并加上自己的Pipeline名称,例如本程序中是下面代码所示的样子,然后最后的数字是0~1000之间就行,表示优先级,具体的可以看注释的网址:D

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {'acg.pipelines.AcgPipeline': 300,
}


最终结果

可以看到最后是完成了我们的愿想:D


总结

1. Python真心强大,这几天还在继续看一些基础的教程,发现很多很好用的语句和内置函数(太TM强大了,让在C和C++摸爬滚打的我十分佩服),还是很值得学习一下

2. Scrapy真心强大,分布式的抓取,速度很快,但是看网上的建议说速度太快也不好,应该加个延时,但是我在跑程序的时候并没有遇到锁IP的情况,因此就没有加延时

3. 整个程序里面有很多不太规范的地方,比如每句后面都加了分号,而且字符串大多数都用的是双影号,主要是为了和C++编程习惯一致,毕竟以后还要靠C++吃饭:x

4. 最后是整个程序的代码,如果想作为参考的可以到GitHub上下载,链接:https://github.com/wuRDmemory/acg_spider


以上的所有都是个人的理解,如果有错误,还请各位大神能够指点纠正,在此不胜感谢!


这篇关于Python初识——Scrapy抓取二次元小姐姐图片的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/526630

相关文章

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

python 字典d[k]中key不存在的解决方案

《python字典d[k]中key不存在的解决方案》本文主要介绍了在Python中处理字典键不存在时获取默认值的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录defaultdict:处理找不到的键的一个选择特殊方法__missing__有时候为了方便起见,

使用Python绘制可爱的招财猫

《使用Python绘制可爱的招财猫》招财猫,也被称为“幸运猫”,是一种象征财富和好运的吉祥物,经常出现在亚洲文化的商店、餐厅和家庭中,今天,我将带你用Python和matplotlib库从零开始绘制一... 目录1. 为什么选择用 python 绘制?2. 绘图的基本概念3. 实现代码解析3.1 设置绘图画

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主