python爬虫反爬机制_Python Scrapy突破反爬虫机制(项目实践)

对于 BOSS 直聘这种网站,当程序请求网页后,服务器响应内容包含了整个页面的 HTML 源代码,这样就可以使用爬虫来爬取数据。但有些网站做了一些“反爬虫”处理,其网页内容不是静态的,而是使用 JavaScript 动态加载的,此时的爬虫程序也需要做相应的改进。

使用 shell 调试工具分析目标站点

本项目爬取的目标站点是 https://unsplash.com/,该网站包含了大量高清、优美的图片。本项目的目标是爬虫程序能自动识别并下载该网站上的所有图片。

在开发该项目之前,依然先使用 Firefox 浏览该网站,然后查看该网站的源代码,将会看到页面的

元素几乎是空的,并没有包含任何图片。

现在使用 Scrapy 的 shell 调试工具来看看该页面的内容。在控制台输入如下命令,启动 shell 调试:

scrapy shell https://unsplash.com/

执行上面命令,可以看到 Scrapy 成功下载了服务器响应数据。接下来,通过如下命令来尝试获取所有图片的 src 属性(图片都是 img 元素,src 属性指定了图片的 URL):

response.xpath('//img/@src').extract()

执行上面命令,将会看到返回一系列图片的URL,但它们都不是高清图片的 URL。

还是通过"Ctrl+Shift+I"快捷键打开 Firefox 的调试控制台,再次向 https://unsplash.com/ 网站发送请求,接下来可以在 Firefox 的调试控制台中看到如图 1 所示的请求。

2-1Z31215534GA.gif

图 1 动态获取图片的请求

可见,该网页动态请求图片的 URL 如下:

https://unsplash.com/napi/photos?page=4&per_page=12

上面 URL 中的 page 代表第几页,per_page 代表每页加载的图片数。使用 Scrapy 的 shell 调试工具来调试该网址,输入如下命令:

scrapy shell https://unsplash.com/napi/photos?page=1&per_page=10

上面命令代表请求第 1 页,每页显示 10 张图片的响应数据。执行上面命令,服务器响应内容是一段 JSON 数据,接下来在 shell 调试工具中输入如下命令:

>>> import json

>>> len(json.loads(response.text))

10

从上面的调试结果可以看到,服务器响应内容是一个 JSON 数组(转换之后对应于 Python 的 list 列表),且该数组中包含 10 个元素。

使用 Firefox 直接请求 https://unsplash.com/napi/photos?page=1&per_page=12 地址(如果希望使用更专业的工具,则可选择 Postman),可以看到服务器响应内容如图 2 所示。

2-1Z3121554429B.gif

图 2 服务器响应的JSON 数据

在图 2 所示为所有图片的 JSON 数据,每张图片数据都包含 id、created_at(创建时间)、updated_at(更新时间)、width(图片宽度)、height(图片高度)等基本信息和一个 links 属性,该属性值是一个对象(转换之后对应于 Python 的 dict),它包含了 self、html、download、download_location 属性,其中 self 代表浏览网页时的图片的 URL;而 download 才是要下载的高清图片的 URL。

网络爬虫毕竟是针对别人的网站“爬取” 数据的,而目标网站的结构随时可能发生改变,读者应该学习这种分析方法,而不是“生搬硬套”照抄本节的分析结果。

尝试在 shell 调试工具中查看第一张图片的下载 URL,应该在 shell 调试工具中输入如下命令:

>>> json.loads(response.text)[0]['links']['download']

'https://unsplash.com/photos/-RMY4j97SsM/download'

与图 2 中 JSON 数据对比不难看出,shell 调试工具输出的第一张图片的下载 URL 与图 2 所显示的第一张图片的下载 URL 完全相同。

由此得到一个结论,该网页加载时会自动向 https://unsplash.com/napi/photos?age=N&per_page=N 发送请求,然后根据服务器响应的 JSON 数据来动态加载图片。

由于该网页是“瀑布流”设计(所谓“瀑布流”设计,就是网页没有传统的分页按钮,而是让用户通过滚动条来实现分页,当用户向下拖动滚动条时,程序会动态载入新的分页),

当我们在 Firefox 中拖动滚动条时,可以在 Firefox 的调试控制台中看到再次向 https://unsplash.com/napi/photos?page=N&per_page=N 发送了请求,

只是 page 参数发生了改变。可见,为了不断地加载新的图片,程序只要不断地向该 URL 发送请求,并改变 page 参数即可。

经过以上分析,下面我们开始正式使用 Scrapy 来实现爬取高清图片。

使用Scrapy 爬取高清图片

按照惯例,使用如下命令来创建一个 Scrapy 项目:

scrapy startproject UnsplashimageSpider

然后在命令行窗口中进入 UnsplashlmageSpider 所在的目录下(不要进入 UnsplashImageSpider\UnsplashImageSpider目录下),执行如下命令来生成 Spider 类:

scrapy genspider unsplash_image 'unsplash.com

上面两个命令执行完成之后,一个简单的 Scrapy 项目就创建好了。

接下来需要修改 UnsplashImageSpider\items.py、UnsplashImageSpider\pipelines.py、UnsplashImageSpider\spiders\unsplash_image.py、UnsplashImageSpider\settings.py 文件,将它们全部改为使用 UTF-8 字符集来保存。

现在按照如下步骤来开发该爬虫项目:

定义 Item 类。由于本项目的目标是爬取高清图片,因此其所使用的 Item 类比较简单,只要保存图片 id 和图片下载地址即可。

下面是该项目的 Item 类的代码:

import scrapy

class ImageItem(scrapy.Item):

# 保存图片id

image_id = scrapy.Field()

# 保存图片下载地址

download = scrapy.Field()

上面程序为 Item 类定义了两个变量,分别用于保存图片 id 和图片下载地址。

开发 Spider。开发 Spider 就是指定 Scrapy 发送请求的 URL,并实现 parse(self, response) 方法来解析服务器响应数据。

下面是该项目的 Spider 程序:

import scrapy, json

from UnsplashImageSpider.items import ImageItem

class UnsplashImageSpider(scrapy.Spider):

# 定义Spider的名称

name = 'unsplash_image'

allowed_domains = ['unsplash.com']

# 定义起始页面

start_urls = ['https://unsplash.com/napi/photos?page=1&per_page=12']

def __init__ (self):

self.page_index = 1

def parse(self, response):

# 解析服务器响应的JSON字符串

photo_list = json.loads(response.text) # ①

# 遍历每张图片

for photo in photo_list:

item = ImageItem()

item['image_id'] = photo['id']

item['download'] = photo['links']['download']

yield item

self.page_index += 1

# 获取下一页的链接

next_link = 'https://unsplash.com/napi/photos?page='\

+ str(self.page_index) + '&per_page=12'

# 继续获取下一页的图片

yield scrapy.Request(next_link, callback=self.parse)

上面程序中第 9 行代码指定的 URL 是本项目爬取的第一个页面,由于该页面的响应是一个 JSON 数据,因此程序无须使用 XPath 或 CSS 选择器来“提取”数据,而是直接使用 json 模块的 loads() 函数来加载该响应数据即可。

在获取 JSON 响应数据之后,程序同样将 JSON 数据封装成 Item 对象后返回给 Scrapy 引擎。

Spider 到底应该使用 XPath 或 CSS 选择器来提取响应数据,还是使用 JSON,完全取决于目标网站的响应内容,怎么方便怎么来!总之,提取到数据之后,将数据封装成 Item 对象后返回给 Scrapy 引擎就对了。

上面程序中倒数第 2 行代码定义了加载下一页数据的 URL,接下来使用 scrapy.Request 向该 URL 发送请求,并指定使用 self.parse 方法来处理服务器响应内容,这样程序就可以不断地请求下一页的图片数据。

开发 Pipeline。Pipeline 负责保存 Spider 返回的 Item 对象(封装了爬取到的数据)。本项目爬取的目标是图片,因此程序得到图片的 URL 之后,既可将这些 URL 地址导入专门的下载工具中批量下载,也可在 Python 程序中直接下载。

本项目的 Pipeline 将使用 urllib.request 包直接下载。下面是该项目的 Pipeline 程序:

from urllib.request import *

class UnsplashimagespiderPipeline(object):

def process_item(self, item, spider):

# 每个item代表一个要下载的图片

print('----------' + item['image_id'])

real_url = item['download'] + "?force=true"

try:

pass

# 打开URL对应的资源

with urlopen(real_url) as result:

# 读取图片数据

data = result.read()

# 打开图片文件

with open("images/" + item['image_id'] + '.jpg', 'wb+') as f:

# 写入读取的数据

f.write(data)

except:

print('下载图片出现错误' % item['image_id'])

上面程序中第 7 行代码用于拼接下载图片的完整地址。可能有读者会问,为何要在图片下载地址的后面追加“?force=true”?这并不是本项目所能决定的,读者可以把鼠标指针移动到 https://unsplash.com 网站中各图片右下角的下载按钮上,即可看到各图片的下载地址都会在 download 后追加“?force=true”,此处只是模拟这种行为而已。

程序中第 11 行代码使用 urlopen() 函数获取目标 URL 的数据,接下来即可读取图片数据,并将图片数据写入下载的目标文件中。

经过上面 3 步,基于 Scrapy 开发的高清图片爬取程序基本完成。接下来依然需要对 settings.py 文件进行修改,即增加一些自定义请求头(用于模拟浏览器),设置启用指定的 Pipeline。下面是本项目修改后的 settings.py 文件:

BOT_NAME = 'UnsplashImageSpider'

SPIDER_MODULES = ['UnsplashImageSpider.spiders']

NEWSPIDER_MODULE = 'UnsplashImageSpider.spiders'

ROBOTSTXT_OBEY = True

# 配置默认的请求头

DEFAULT_REQUEST_HEADERS = {

"User-Agent" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0",

'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'

}

# 配置使用Pipeline

ITEM_PIPELINES = {

'UnsplashImageSpider.pipelines.UnsplashimagespiderPipeline': 300,

}

至此,这个可以爬取高清图片的爬虫项目开发完成,读者可以在 UnsplashlmageSpider 目录下执行如下命令来启动爬虫。

scrapy crawl unsplash_image

#或者

scrapy runspider unsplash_image

区别:

命令

说明

是否需要项目

示例

runspider

未创建项目的情况下,运行一个编写在Python文件中的spider

no

$ scrapy runspider myspider.py

crawl

使用spider进行爬取

yes

$ scrapy crawl myspider

运行该爬虫程序之后,可以看到在项目的 images 目录下不断地增加新的高清图片(对图片的爬取速度在很大程度上取决于网络下载速度),这些高清图片正是 https://unsplash.com 网站中所展示的图片。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自定义URL Scheme完全指南

iPhone / iOS SDK 最酷的特性之一就是应用将其自身”绑定”到一个自定义 URL scheme 上,该 scheme 用于从浏览器或其他应用中启动本应用。 注册自定义 URL Scheme 注册自定义 URL Scheme 的第一步是创建 URL Scheme — 在 Xcode Project Navigator 中找到并点击工程…

P6 音频格式—— AAC

目录 前言 01 AAC是什么? 02 为什么需要进行AAC进行音频压缩处理? 03 AAC的特点以及优势 04 AAC格式详解: 4.1. ADIF的数据结构: 4.1.1 ADIF Header具体的表格: 4.2. ADTS的结构(重点): …

Android开发笔记——ListView模块、缓存及性能

ListView是Android开发中最常用的组件之一。本文将重点说明如何正确使用ListView,以及使用过程中可能遇到的问题。 ListView开发模块图片缓存可能遇到的问题一、ListView开发模块 从项目实践的角度来看,ListView适合“自底向上”的开发模式,即…

python实现excel筛选功能并输出_python如何实现excel按颜色筛选功能

离岛 2020-07-09 09:37 已采纳 不太了解具体需求,提供一些示例代码和思路供你参考: 整体思路:首先已知excel中的颜色值,根据编码实现颜色筛选的功能 示例: 1、首先安装pip install openpyxl 2、示例代码可以获取Excel中…

C学习杂记(七)extern声明可省略变量类型

工作三年,看C的书也不少。第一次知道extern可以省略变量类型。 b.c有一个全局变量unsigned int data_length,a.c想要调用它,通常使用: extern unsigned int data_length; 在声明时可以把外部变量类型去掉:extern da…

linux 内存取证_【取证流程】电子数据证据远程勘验

原创:弘连网络电子数据证据远程勘验在日常的取证工作中必不可少,但由于存在信息安全差、数据可能被篡改的问题。取证过程中,有明确的取证要求来确保取证过程的规范显得至关重要,今天我们就一起来回顾下遇到远程勘验的取证场景&…

python2 print_Python2和Python3中print的不同点

在Python2和Python3中都提供print()方法来打印信息,但两个版本间的print稍微有差异 主要体现在以下几个方面: 1.python3中print是一个内置函数,有多个参数,而python2中print是一个语法结构; 2.Python2打印时可以不加括号&#xff…

java 与 c#的 中 字符串比较“==”与“equals”的差异

.net中,其字符串特有的驻留机制,保证了在同一进程中,相同字符序列的字符串,只有一个实例,这样能避免相同内容的字符串重复实例化,以减少性能开销。 先来回顾一下c#中的代码: public static void…

visual studio 2019 未能在命名空间“System.IO.Ports”中找到类型名“SerialPort”

在vs2019以前的版本,只要using System.IO.Ports就可以用SerialPort。 这里需要自己手动添加相关引用。 工具–>Nuget包管理器(N)–>管理解决方案的Nuget程序包(N) –>浏览,左边搜索SerialPort 右…

python开发环境功能介绍_第一模块 第3章 Python介绍与环境配置

python入门(全为重点) 1. 编程语言介绍 编程语言分类、总结 2. python介绍 3. 解释器多版本共存 4. 运行python程序的两种方式 5. 一个python程序运行的三个步骤(******) 6. 注释 7. IED集成开发环境 3.1 编程语言分类之低级语言 这里的高级/低级指的是离…

用WPF做关于MEF 简单学习记录

写在前面:下面学习所得多是从自http://www.cnblogs.com/comsokey/p/MEF1.html和http://www.cnblogs.com/yunfeifei/p/3922668.html两位大神的文章里学到的,特别鸣谢!整理下是更大一方面是对自己知识的梳理,用词用句不够准确&#…

C排序算法:(一)直接排序

最简单的排序方法。 如果从大到小排序&#xff0c;那么从[0]元素开始&#xff0c;和后面的元素进行对比&#xff0c;如果后面元素大&#xff0c;则和[0]元素交换。 核心思想&#xff1a;选定基准元素&#xff0c;和其他元素对比。 #include <stdio.h>unsigned char buff…

c语言 链表_C语言编程第22讲——单向有序链表的C语言实现

1、单向有序链表的含义单向有序链表可以解析为四个名词&#xff1a;表&#xff1a;一组元素&#xff1b;链表&#xff1a;表中的元素不是从前往后一个挨着一个&#xff0c;而是通过一个元素才能找到另一个元素&#xff1b;单向&#xff1a;表中的元素只能从前往后访问&#xff…

C排序算法:(二)冒泡排序

冒泡排序就是从左至右比较相邻的两个数值大小&#xff0c;如果右侧的数值较小&#xff0c;则交换两个数值的位置&#xff0c;较大的数值就会像泡泡一样一路向右漂浮。 #include <stdio.h>//small to big void Bubble_Sort(unsigned char *input_data, unsigned int inpu…

pythonturtle画点的指令_简述python的turtle绘画命令及解释

一 基础认识 turtle库是python的标准库之一&#xff0c;它是一个直观有趣的图形绘制数据库&#xff0c;turtle(海龟&#xff09;图形绘制的概念诞生1969年。它的应用十分广&#xff0c;而且使用简单&#xff0c;只要在编写python程序时写上import turtle即可。 1.绘图窗口设置命…

OpenGL ES一些函数详解(一)

glLoadIdentity和glMultMatrix glLoadIdentity的作用是将当前模型视图矩阵转换为单位矩阵&#xff08;行数和列数相同的矩阵&#xff0c;并且矩阵的左上角至右下角的连线上的元素都为1&#xff0c;其它元素都为0&#xff09;&#xff0c;这样可以方便矩阵的乘法运算。 glMultMa…

C排序算法:(三)插入排序

像打扑克牌一样&#xff0c;每次摸一张牌&#xff0c;把牌插入正确位置。 #include <stdio.h>void Insert_Sort_Small_To_Big(unsigned char* input_data, unsigned int input_length) {int i 0, j 0;unsigned char temp 0;for (i 1; i < input_length; i){temp …

LeetCode Binary Tree Paths(简单题)

题意&#xff1a;   给出一个二叉树&#xff0c;输出根到所有叶子节点的路径。 思路&#xff1a; 直接DFS一次&#xff0c;只需要判断是否到达了叶子&#xff0c;是就收集答案。 1 /**2 * Definition for a binary tree node.3 * struct TreeNode {4 * int val;5 * …

新唐M0 KEIL环境搭建,找不到device不识别,关键:Nu-Link_Keil_Driver

公司用新唐的芯片&#xff0c;网上关于新唐的资料相对ST确实少得可怜&#xff0c;搭建环境也是搞了好久&#xff0c;去keil官网下载安装了新唐的pack 打开官方参考工程还是提示找不到device&#xff0c;option里面也没有识别出来。 百度了好久&#xff0c;尝试了各种办法&…

c语言有趣代码_【新课预知】——C语言程序设计

编者按&#xff1a;如今疫情这么严重&#xff0c;为了保护好自己&#xff0c;为了身边的人&#xff0c;大家可一定要听从国家号召——“宅”起来&#xff01;并且&#xff0c;小编们为了让大家伙“宅”得更加健康&#xff0c;可是费了不少脑筋呢&#xff0c;学哉数理公众号首次…