您的位置 首页 php

高级网络爬虫技术:绕过 “403 Forbidden”,验证码等

想要学习Python。关注小编头条号,私信【学习资料】,即可免费领取一整套系统的板Python学习教程!

建立工程项目

我们会在 virtualenv 中建立我们的项目,这可以让我们封装一下依赖关系。首先我们在~/scrapers/zipru中创建一个virtualenv ,并且安装 scrapy 包。

mkdir ~/scrapers/zipru
cd ~/scrapers/zipru
virtualenv env
. env/bin/activate
pip install scrapy
 

你运行的终端将被配置为使用本地的virtualenv。如果你打开另一个终端,那么你就需要再次运行. ~/scrapers/zipru/env/bin/active 命令 (否则你有可能得到命令或者模块无法找到的错误消息)。

现在你可以通过运行下面的命令来创建一个新的项目框架:

scrapy startproject zipru_scraper
 

这样就会创建下面的目录结构。

└── zipru_scraper
 ├── zipru_scraper
 │ ├── __init__.py
 │ ├── items.py
 │ ├── middlewares.py
 │ ├── pipelines.py
 │ ├── settings.py
 │ └── spiders
 │ └── __init__.py
 └── scrapy.cfg
 

大多数默认情况下产生的这些文件实际上不会被用到,它们只是建议以一种合理的方式来构建我们的代码。从现在开始,你应该把 ~/scrapers/zipru/zipru_scraper 当做这个项目的根目录。这里是任何scrapy命令运行的目录,同时也是所有相对路径的根。

添加一个基本的爬虫功能

现在我们需要添加一个Spieder类来让我们的scrapy真正地做一些事情。Spider类是scrapy爬虫用来解析文本,爬取新的url链接或是提取数据的一个类。我们非常依赖于默认Spider类的实现,以最大限度地减少我们必须要编写的代码量。这里要做的事情看起来有点自动化,但假如你看过文档,事情会变得更加简单。

首先,在zipru_scraper/spiders/目录下创建一个文件,命名为 zipru_spider.py ,输入下面内容。

import scrapy
class ZipruSpider(scrapy.Spider):
 name = 'zipru'
 start_urls = ['
 

你可以在上面的网页中看到许多指向其他页面的连接。我们想让我们的爬虫 跟踪 这些链接,并且解析他们的内容。为了完成这个任务,我们首 先需 要识别出这些链接并且弄清楚他们指向的位置。

在这个阶段,DOM检查器将起到很大的助力。如果你右击其中的一个页面链接,在DOM检查器里面查看它,然后你就会看到指向其他页面的链接看起来像是这样的:

<a href="/torrents.php?...page=2" title="page 2">2</a>
<a href="/torrents.php?...page=3" title="page 3">3</a>
<a href="/torrents.php?...page=4" title="page 4">4</a>
 

接下来我们需要为这些链接构造一个选择器表达式。有几种类型似乎用css或者xpath选择器进行搜索更适合,所以我通常倾向于灵活地混合使用这几种选择器。我强烈推荐学习xpath,但是不幸的是,它有点超出了本教程的范围。我个人认为xpath对于网络爬虫,web UI 测试,甚至一般的web开发来说都是不可或缺的。我接下来仍然会使用css选择器,因为它对于大多数人来说可能比较熟悉。

要选择这些页面链接,我们可以把 a[title ~= page] 作为一个 css 选择器,来查找标题中有 “page” 字符的 <a>标签。如果你在 DOM 检查器中按 ctrl-f,那么你就会发现你也可以使用这个css表达式作为一条查找语句(也可以使用xpath)。这样我们就可以循环查看所有的匹配项了。这是一个很棒的方法,可以用来检查一个表达式是否有效,并且表达式足够明确不会在不小心中匹配到其他的标签。我们的页面链接选择器同时满足了这两个条件。

为了讲解我们的爬虫是怎样发现其他页面的,我们在 ZipruSpider 类中添加一个 parse(response) 方法,就像下面这样:

def parse(self, response):
 # proceed to other pages of the listings
 for page_url in response.css('a[title ~= page]::attr( href )').extract():
 page_url = response.urljoin(page_url)
 yield scrapy. request (url=page_url, callback=self.parse)
 

当我们开始爬取的时候,我们添加到 start_urls 中的链接将会被自动获取到,响应内容会被传递到 parse(response) 方法中。之后我们的代码就会找到所有指向其他页面的链接,并且产生新的请求对象,这些请求对象将使用同一个 parse(response) 作为回调函数。这些请求将被转化成响应对象,只要 url 仍然产生,响应就会持续地返回到 parse(response) 函数(感谢去重器)。

我们的爬虫已经可以找到了页面中列出的所有不同的页面,并且对它们发出了请求,但我们仍然需要提取一些对爬虫来说有用的数据。torrent 列表位于 <table> 标签之内,并且有属性 class=”list2at” ,每个单独的 torrent 都位于带有属性 class=”lista2″ 的 <tr> 标签,其中的每一行都包含 8 个 < td >标签,分别与 “类别”,“文件”,“添加时间”,“文件大小”,“保种的人”,“下载文件的人”,“文件描述”,和“上传者”相对应。在代码中查看其它的细节可能是最简单的方法,下面是我们修改后的 parse(response) 方法:

def parse(self, response):
 # proceed to other pages of the listings
 for page_url in response.xpath('//a[contains(@title, "page ")]/@href').extract():
 page_url = response.urljoin(page_url)
 yield scrapy.Request(url=page_url, callback=self.parse)
 
 # extract the torrent items
 for tr in response.css('table.lista2t tr.lista2'):
 tds = tr.css('td')
 link = tds[1].css('a')[0]
 yield {
 'title' : link.css('::attr(title)').extract_first(),
 'url' : response.urljoin(link.css('::attr(href)').extract_first()),
 'date' : tds[2].css('::text').extract_first(),
 'size' : tds[3].css('::text').extract_first(),
 'seeders': int(tds[4].css('::text').extract_first()),
 'leechers': int(tds[5].css('::text').extract_first()),
 'uploader': tds[7].css('::text').extract_first(),
 }
 

我们的 parse(response) 方法现在能够返回字典类型的数据,并且根据它们的类型自动区分请求。每个字典都会被解释为一项,并且作为爬虫数据输出的一部分。

如果我们只是爬取大多数常见的网站,那我们已经完成了。我们只需要使用下面的命令来运行:

scrapy crawl zipru -o torrents.jl
 

几分钟之后我们本应该得到一个 [JSON Lines] 格式 torrents.jl 文件,里面有我们所有的torrent 数据。取而代之的是我们得到下面的错误信息(和一大堆其他的东西):

[scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
[scrapy.extensions.telnet]  DEBUG : Telnet console listening on 127.0.0.1:6023
[scrapy.core.engine] DEBUG: Crawled (403) <GET  (referer: None) ['partial']
[scrapy.core.engine] DEBUG: Crawled (403) <GET  (referer: None) ['partial']
[scrapy.spidermiddlewares.httperror] INFO: Ignoring response <403  HTTP status code is not handled or not allowed
[scrapy.core.engine] INFO: Closing spider (finished)
 

我好气啊!我们现在必须变得更聪明来获得我们完全可以从公共API得到的数据,因为上面的代码永远都无法爬取到那些数据。

简单的问题

我们的第一个请求返回了一个 403 响应,所以这个url被爬虫忽略掉了,然后一切都关闭了,因为我们只给爬虫提供了一个 url 链接。同样的请求在网页浏览器里运行正常,即使是在没有会话(session)历史的隐匿模式也可以,所以这一定是由于两者请求头信息的差异造成的。我们可以使用 tcpdump 来比较这两个请求的头信息,但其实有个常见错误,所以我们应该首先检查: user agent 。

Scrapy 默认把 user-agent 设置为 “Scrapy/1.3.3 (+“,一些服务器可能会屏蔽这样的请求,甚至使用白名单只允许少量的user agent 通过。你可以在线查看 最常见的 user agent ,使用其中任何一个通常就足以绕过基本反爬虫策略。选择一个你最喜欢的 User-agent ,然后打开 zipru_scraper/settings.py ,替换 User agent

# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'zipru_scraper (+
 

使用下面内容替换 USER_AGENT :

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36'
 

你可能注意到了,默认的scrapy设置中有一些令爬虫蒙羞的事。关于这个问题的观点众说纷纭,但是我个人认为假如你想让爬虫表现的像是一个人在使用普通的网页浏览器,那么你就应该把你的爬虫设置地像普通的网络浏览器那样。所以让我们一起添加下面的设置来降低一下爬虫响应速度:

CONCURRENT_REQUESTS = 1
DOWNLOAD_DELAY = 5
 

通过 AutoThrottle 扩展 ,上面的设置会创建一个稍微真实一点的浏览模式。我们的爬虫在默认情况下会遵守 robots.txt,所以现在我们的行为非常检点。

现在使用 scrapy crawl zipru -o torrents.jl 命令再次运行爬虫,应该会产生下面的输出:

[scrapy.core.engine] DEBUG: Crawled (200) <GET  (referer: None)
[scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET  from <GET 
[scrapy.core.engine] DEBUG: Crawled (200) <GET  (referer: None) ['partial']
[scrapy.core.engine] INFO: Closing spider (finished)
 

这是一个巨大的进步!我们获得了两个 200 状态码和一个 302状态码,下载 中间件 知道如何处理 302 状态码。不幸的是,这个 302 将我们的请求重定向到了一个看起来不太吉利的页面threat_defense.php。不出所料,爬虫没有发现任何有用的东西,然后爬虫就停止运行了。

注: 假如网站检测到你的爬虫,那么网站就会把你的请求重定向到 threat_defense.php 页面,使你的爬虫失效,用来防止频繁的爬虫请求影响了网站正常用户的使用。

下载中间件

在我们深入研究我们目前所面临的更复杂的问题之前,先了解一下请求和响应在爬虫中是怎样被处理的,将会很有帮助。当我们创建了我们基本的爬虫,我们生成了一个 scrapy.Request 对象,然后这些请求会以某种方法转化为与服务器的响应相对应的 scrapy.Response 对象。这里的 “某种方法” 很大一部分是来自于下载中间件。

下载中间件继承自 scrapy.downloadermiddlewares.DownloaderMiddleware 类并且实现了process_request(request, spider) 和 process_response(request, response, spider) 方法。你大概可以从他们的名字中猜到他们是做什么的。实际上这里有一大堆的默认开启的中间件。下面是标准的中间件配置(你当然可以禁用、添加或是重新设置这些选项):

DOWNLOADER_MIDDLEWARES_BASE = {
 'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
 'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
 'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
 'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
 

当一个请求到达服务器时,他们会通过每个这些中间件的 process_request(request, spider) 方法。 这是按照数字顺序发生的,RobotsTxtMiddleware 中间件首先产生请求,并且HttpCacheMiddleware 中间件最后产生请求。一旦接收到一个响应,它就会通过任何已启用的中间件的 process_response(request,response,spider) 方法来返回响应。这次是以相反的顺序发生的,所以数字越高越先发送到服务器,数字越低越先被爬虫获取到。

一个特别简单的中间件是 CookiesMiddleware。它简单地检查响应中请求头的 Set-Cookie,并且保存 cookie 。然后当响应返回的时候,他们会适当地设置 Cookie 请求头标记,这样这些标记就会被包含在发出的请求中了。这个由于时间太久的原因要比我们说的要稍微复杂些,但你会明白的。

另一个相对基本的就是 RedirectMiddleware 中间件,它是用来处理 3XX 重定向的。它让一切不是 3XX 状态码的响应都能够成功的通过,但假如响应中还有重定向发生会怎样? 唯一能够弄清楚服务器如何响应重定向URL的方法就是创建一个新的请求,而且这个中间件就是这么做的。当 process_response(request, response, spider) 方法返回一个请求对象而不是响应对象的时候,那么当前响应就会被丢弃,一切都会从新的请求开始。这就是 RedirectMiddleware 中间件怎样处理重定向的,这个功能我们稍后会用到。

如果你对于有那么多的中间件默认是开启的感到惊讶的话,那么你可能有兴趣看看 体系架构概览。实际上同时还有很多其他的事情在进行,但是,再说一次,scrapy的最大优点之一就是你不需要知道它的大部分原理。你甚至不需要知道下载中间件的存在,却能写一个实用的爬虫,你不必知道其他部分就可以写一个实用的下载中间件。

困难的问题

回到我们的爬虫上来,我们发现我们被重定向到某个 threat_defense.php?defense=1&…URL上,而不是我们要找的页面。当我们在浏览器里面访问这个页面的时候,我们看到下面的东西停留了几秒:

在被重定向到 threat_defense.php?defense=2&… 页面之前,会出现像下面的提示:

看看第一个页面的源代码就会发现,有一些 javascript 代码负责构造一个特殊的重定向URL,并且构造浏览器的cookies。如果我们想要完成这个任务,那我们就必须同时解决上面这两个问题。

接下来,当然我们也需要解决验证码并提交答案。如果我们碰巧弄错了,那么我们有时会被重定向到另一个验证码页面,或者我们会在类似于下面的页面上结束访问:

在上面的页面中,我们需要点击 “Click here” 链接来开始整个重定向的循环,小菜一碟,对吧?

我们所有的问题都源于最开始的 302 重定向,因此处理它们的方法自然而然应该是做一个自定义的 重定向中间件。我们想让我们的中间件在所有情况下都像是正常重定向中间件一样,除非有一个 302 状态码并且请求被重定向到 threat_defense.php 页面。当它遇到特殊的 302 状态码时,我们希望它能够绕过所有的防御机制,把访问cookie添加到 session 会话中,最后重新请求原来的页面。如果我们能够做到这一点,那么我们的Spider类就不必知道这些事情,因为请求会全部成功。

打开 zipru_scraper/middlewares.py 文件,并且把内容替换成下面的代码:

import os, tempfile, time, sys, logging
logger = logging.getLogger(__name__)
 
import dryscrape
import pytesseract
from PIL import Image
 
from scrapy.downloadermiddlewares.redirect import RedirectMiddleware
 
class ThreatDefenceRedirectMiddleware(RedirectMiddleware):
 def _redirect(self, redirected, request, spider, reason):
 # act normally if this isn't a threat defense redirect
 if not self.is_threat_defense_url(redirected.url):
 return super()._redirect(redirected, request, spider, reason)
 
 logger.debug(f'Zipru threat defense triggered for {request.url}')
 request.cookies = self.bypass_threat_defense(redirected.url)
 request.dont_filter = True # prevents the original link being marked a dupe
 return request
 
 def is_threat_defense_url(self, url):
 return '://zipru.to/threat_defense.php' in url
 

你可能注意到我们继承了 RedirectMiddleware 类,而不是直接继承 DownloaderMiddleware 类。这样就允许我们重用大部分的重定向处理函数,并且把我们的代码插入到 _redirect(redirected, request, spider, reason) 函数中,一旦有重定向的请求被创建,process_response(request, response, spider) 函数就会调用这个函数。我们只是把对于普通的重定向的处理推迟到父类进行处理,但是对于特殊的威胁防御重定向的处理是不一样的。我们到目前为止还没有实现 bypass_threat_defense(url) 方法,但是我们可以知道它应该返回访问cookies,并把它附加到原来的请求中,然后原来的请求将被重新处理。

为了开启我们新的中间件,我们需要把下面的内容添加到 zipru_scraper/settings.py中:

DOWNLOADER_MIDDLEWARES = {
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': None,
 'zipru_scraper.middlewares.ThreatDefenceRedirectMiddleware': 600,
}
 

这会禁用默认的重定向中间件,并且把我们的中间件添加在中间件堆栈中和默认重定向中间件相同的位置。我们必须安装一些额外的包,虽然我们现在没有用到,但是稍后我们会导入它们:

pip install dryscrape # headless webkit
pip install Pillow # image processing
pip install pytesseract # OCR
 

请注意,这三个包都有 pip 无法处理的外部依赖,如果你运行出错,那么你可能需要访问 dryscrape, Pillow, 和 pytesseract 的安装教程,遵循平台的具体说明来解决。

我们的中间件现在应该能够替代原来的标准重定向中间件,现在我们只需要实现 bypass_thread_defense(url) 方法。我们可以解析 javascript 代码来得到我们需要的变量,然后用 python 重建逻辑,但这看起来很不牢靠,而且需要大量的工作。让我们采用更简单的方法,尽管可能还是比较笨重,使用无头的 webkit 实例。有几个不同选择,但我个人比较喜欢 dryscrape (我们已经在上面安装了)

首先,让我们在中间件构造函数中初始化一个 dryscrape 会话。

def __init__(self, settings):
 super().__init__(settings)
 
 # start xvfb to support headless scraping
 if 'linux' in sys.platform:
 dryscrape.start_xvfb()
 
 self.dryscrape_session = dryscrape.Session(base_url='
 

你可以把这个会话对象当做是一个单独的浏览器标签页,它可以完成一切浏览器通常可以做的事情(例如:获取外部资源,执行脚本)。我们可以在新的标签页中打开新的 URL 链接,点击一些东西,或者在输入框中输入内容,或是做其他的各种事情。Scrapy 支持并发请求和多项处理,但是响应的处理是单线程的。这意味着我们可以使用这个单独的 dryscrapy 会话,而不必担心线程安全。

现在让我们实现绕过威胁防御机制的基本逻辑。

def bypass_threat_defense(self, url=None):
 # only navigate if any explicit url is provided
 if url:
 self.dryscrape_session.visit(url)
 
 # solve the captcha if there is one
 captcha_images = self.dryscrape_session.css('img[src *= captcha]')
 if len(captcha_images) > 0:
 return self.solve_captcha(captcha_images[0])
 
 # click on any explicit retry links
 retry_links = self.dryscrape_session.css('a[href *= threat_defense]')
 if len(retry_links) > 0:
 return self.bypass_threat_defense(retry_links[0].get_attr('href'))
 
 # otherwise, we're on a redi rect  page so wait for the redirect and try again
 self.wait_for_redirect()
 return self.bypass_threat_defense()
 
 def wait_for_redirect(self, url = None, wait = 0.1, timeout=10):
 url = url or self.dryscrape_session.url()
 for i in range(int(timeout//wait)):
 time.sleep(wait)
 if self.dryscrape_session.url() != url:
 return self.dryscrape_session.url()
 logger.error(f'Maybe {self.dryscrape_session.url()} isn't a redirect URL?')
 raise Exception('Timed out on the zipru redirect page.')
 

这样就处理了我们在浏览器中遇到的所有不同的情况,并且完全符合人类在每种情况中的行为。在任何给定情况下采取的措施都取决于当前页面的情况,所以这种方法可以稍微优雅一点地处理各种不同的情况。

最后一个难题是如果如何解决验证码。网上提供了 验证码识别 服务,你可以在必要时使用它的API,但是这次的这些验证码非常简单,我们只用 OCR 就可以解决它。使用 pytessertact 的 OCR 功能,最后我们可以添加 solve_captcha(img) 函数,这样就完善了 bypass_threat_defense() 函数。

def solve_captcha(self, img, width=1280, height=800):
 # take a screenshot of the page
 self.dryscrape_session.set_viewport_size(width, height)
 filename = tempfile.mktemp('.png')
 self.dryscrape_session.render(filename, width, height)
 
 # inject javascript to find the bounds of the captcha
 js = 'document.querySelector("img[src *= captcha]").getBoundingClientRect()'
 rect = self.dryscrape_session.eval_script(js)
 box = (int(rect['left']), int(rect['top']), int(rect['right']), int(rect['bottom']))
 
 # solve the captcha in the screenshot
 image = Image.open(filename)
 os.unlink(filename)
 captcha_image = image.crop(box)
 captcha = pytesseract.image_to_string(captcha_image)
 logger.debug(f'Solved the Zipru captcha: "{captcha}"')
 
 # submit the captcha
 input = self.dryscrape_session.xpath('//input[@id = "solve_string"]')[0]
 input.set(captcha)
 button = self.dryscrape_session.xpath('//button[@id = "button_submit"]')[0]
 url = self.dryscrape_session.url()
 button.click()
 
 # try again if it we redirect to a threat defense URL
 if self.is_threat_defense_url(self.wait_for_redirect(url)):
 return self.bypass_threat_defense()
 
 # otherwise return the cookies as a dict
 cookies = {}
 for cookie_string in self.dryscrape_session.cookies():
 if 'domain=zipru.to' in cookie_string:
 key, value = cookie_string.split(';')[0].split('=')
 cookies[key] = value
 return cookies
 

你可能注意到如果验证码因为某些原因识别失败的话,它就会委托给 back to the bypass_threat_defense() 函数。这样就给了我们多次识别验证码的机会,但重点是,我们会在得到正确结果之前一直在验证码识别过程中循环。

这应该足够让我们的爬虫工作,但是它有可能陷入死循环中。

[scrapy.core.engine] DEBUG: Crawled (200) <GET  (referer: None)
[zipru_scraper.middlewares] DEBUG: Zipru threat defense triggered for 
[zipru_scraper.middlewares] DEBUG: Solved the Zipru captcha: "UJM39"
[zipru_scraper.middlewares] DEBUG: Zipru threat defense triggered for 
[zipru_scraper.middlewares] DEBUG: Solved the Zipru captcha: "TQ9OG"
[zipru_scraper.middlewares] DEBUG: Zipru threat defense triggered for 
[zipru_scraper.middlewares] DEBUG: Solved the Zipru captcha: "KH9A8"
...
 

至少看起来我们的中间件已经成功地解决了验证码,然后补发了请求。问题在于,新的请求再次触发了威胁防御机制。我第一个想法是我可能在怎样解析或是添加cookie上面有错误,但是我检查了三次,代码是正确的。这是另外一种情况 “唯一可能不同的事情就是请求头” 。

很明显,scrapy 和 dryscrape 的请求头都绕过了最初的触发 403 响应的过滤器,因为我们现在不会得到任何 403 的响应。这肯定是因为它们的请求头信息不一致导致的。我的猜测是其中一个加密的访问cookies包含了整个请求头信息的散列值,如果这个散列不匹配,就会触发威胁防御机制。这样的目的可能是防止有人把浏览器的cookie复制到爬虫中去,但是它只是增加了你需要解决的问题而已。

所以让我们在 zipru_scraper/settings.py 中把请求头信息修改成下面这个样子。

DEFAULT_REQUEST_HEADERS = {
 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 'User-Agent': USER_AGENT,
 'Connection': 'Keep-Alive',
 'Accept-Encoding': 'gzip, deflate',
 'Accept-Language': 'en-US,*',
}
 

注意我们已经把 User-Agent 头信息修改成了我们之前定义的 USER_AGENT 中去.这个工作是由 user agent 中间件自动添加进去的,但是把所有的这些配置放到一个地方可以使得 dryscrape 更容易复制请求头信息。我们可以通过修改 ThreatDefenceRedirectMiddleware 初始化函数像下面这样:

def __init__(self, settings):
 super().__init__(settings)
 
 # start xvfb to support headless scraping
 if 'linux' in sys.platform:
 dryscrape.start_xvfb()
 
 self.dryscrape_session = dryscrape.Session(base_url='
 for key, value in settings['DEFAULT_REQUEST_HEADERS'].items():
 # seems to be a bug with how webkit-server handles accept-encoding
 if key.lower() != 'accept-encoding':
 self.dryscrape_session.set_header(key, value)
 

现在,当我们可以通过命令 scrapy crawl zipru -o torrents.jl 再次运行爬虫。我们可以看到源源不断的爬取的内容,并且我们的 torrents.jl 文件记录把爬取的内容全部记录了下来。我们已经成功地绕过了所有的威胁防御机制。

总结

我们已经成功地写了一个能够解决四种截然不同的威胁防御机制的爬虫,这四种防御机制分别是:

  1. User agent 过滤
  2. 模糊的 Javascript 重定向
  3. 验证码
  4. 请求头一致性检查

我们的目标网站 Zipru 可能是虚构的,但是这些机制都是你会在真实网站上遇到的真实的反爬虫技术。希望我们使用的方法对你自己爬虫中遇到的挑战有帮助。

想要学习Python。关注小编头条号,私信【学习资料】,即可免费领取一整套系统的板Python学习教程!

文章来源:智云一二三科技

文章标题:高级网络爬虫技术:绕过 “403 Forbidden”,验证码等

文章地址:https://www.zhihuclub.com/151700.shtml

关于作者: 智云科技

热门文章

评论已关闭

4条评论

  1. I all the time used to read paragraph in news papers but now as I am a user of web therefore from now I am using net for content,
    thanks to web.

  2. 120 However, trastuzumab does not inhibit the heterodimerization of HER2 with other members of the HER family, especially HER3

网站地图