返回介绍

12.3 网络:互联网的工作原理 以及为什么它会让脚本崩溃

发布于 2024-01-27 21:43:11 字数 4965 浏览 0 评论 0 收藏 0

取决于运行抓取脚本的频率,以及每个脚本工作的重要性,你可能会碰到网络问题。是的,互联网正在尝试破坏你的脚本。为什么?因为互联网认为如果你真的在乎,你会重试。在网页抓取世界里丢失的连接、代理问题以及超时问题普遍存在。然而,有一些方法可以缓解这些问题。

在浏览器中,如果有页面信息没有正确加载,你就会点击刷新,立即发送另一个请求。对于抓取器,你可以模仿这种行为。如果你正在使用 Selenium,刷新内容会极其简单。 Selenium 的 webdriver 对象有一个 refresh 函数,就像浏览器一样。如果你已经填充了一个表单,需要重新提交表单,前进至下一页(有时这类似于浏览器的行为)。如果你需要同警告或弹出窗口交互,Selenium 提供了接受或拒绝信息所需的工具。

Scrapy 有内置的重试中间件。要启用它,你只需将它添加到项目的 settings.py 文件的中间件列表中。中间件(https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#module-scrapy.contrib.downloadermiddleware.retry)希望你在设置中设定一些默认值,以便它知道哪些 HTTP 响应码需要重试(例如,它需要只在返回码为 500 的时候重试吗?),以及重试的次数。

 如果你没有指定这些值,它仍然会使用文档中列出的默认值工作。如果你看到网络错误,然后下载等待时间(另外一个全局设置变量)变长,建议你从 10 次重试开始,或者检查收到的返回码,看看脚本是否访问网站过于频繁。

如果你正在使用自己的 Python 脚本和 LXML 或 BeautifulSoup,最好是捕获这些错误并确定处理它们的方法。大多数时间里,你会注意到 urllib2.HTTPError(https://docs.python.org/2/library/urllib2.html#urllib2.HTTPError)异常的优势;或者,如果你正在使用 requests,代码不会加载内容,并且失败。在 Python 中使用一个 try...except 代码块,你的代码可能看起来像下面一样。

import requests
import urllib2


resp = requests.get('http://sisinmaru.blog17.fc2.com/')

if resp.status_code == 404: ➊
  print 'Oh no!!! We cannot find Maru!!'
elif resp.status_code == 500:
  print 'Oh no!!! It seems Maru might be overloaded.'
elif resp.status_code in [403, 401]:
  print 'Oh no!! You cannot have any Maru!'


try:
  resp = urllib2.urlopen('http://sisinmaru.blog17.fc2.com/') ➋
except urllib2.URLError: ➌
  print 'Oh no!!! We cannot find Maru!!'
except urllib2.HTTPError, err: ➍
  if err.code == 500: ➎
    print 'Oh no!!! It seems Maru might be overloaded.'
  elif err.code in [403, 401]:
    print 'Oh no!! You cannot have any Maru!'
  else:
    print 'No Maru for you! %s' % err.code ➏
except Exception as e: ➐
  print e

❶ 当使用 requests 库来查找网络错误时,检查响应的 status_code。这个属性会返回一个代表在 HTTP 响应中收到的代码的整数。这行代码测试响应是否为 404 错误。

❷ 如果正在使用 urllib2,将请求放在一个 try 语句中(正如这行代码一样)。

❸ 可能会从 urllib2 中看到的一个异常是 URLError。编写一个捕获方法是好的想法。如果它不能解析域名,可能会抛出这个错误。

❹ 可能看到的另外一个异常是 HTTPError。任何有关 HTTP 请求错误的响应都会抛出这个错误。通过添加冒号和 err,捕获了错误,并且将它保存在变量 err 中,这样可以打印错误到日志。

❺ 现在捕捉到了错误,并且将其赋值给前行代码中的 err,这行代码判断 code 属性,来查看 HTTP 错误码。

❻ 对于所有其他的 HTTP 错误,这行代码使用 else 通过格式化它到字符串,来展示错误码。

❼ 这行代码捕获其他所有可能碰到的错误,并且展示错误信息。赋值异常给变量 e,并且打印它,这样可以阅读异常信息。

巧妙地设计脚本,让它尽可能地抗失败,这是一个重要的步骤(第 14 章会更详细地讨论);同时确保在代码中有合适的 try...except 代码块来解释错误,也是进程中重要的一部分。除了 HTTP 错误,有些时候,页面花费过长的时间来加载。如果抓取器响应缓慢或碰到了延迟问题,我们可能会调整超时时间。

 什么是延迟?从网络角度来讲,这是数据从一个地点发送到另一个地点需要的时间。往返延迟是从计算机发送请求到服务器,并且得到响应花费的时间。延迟因为数据的传输而存在,有时数据需要传送几千千米,来完成请求。

当编写和规模化脚本时,考虑延迟是很好的。如果脚本所连接的站点托管在另一个国家,你会经历网络延迟。因此你需要相应地调整超时时间,或者创建一个距离目标端点近的服务器。如果想要添加超时时间到 Selenium 和 Ghost.py 脚本,你可以在抓取工作开始的时候,直接添加到脚本中。对于 Selenium,使用 set_page_load_timeout(http://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.set_page_load_timeout)方法,或使用隐式 / 显式的等待(http://selenium-python.readthedocs.io/waits.html),这样浏览器会等待代码中特定的部分加载。对于 Ghost.py,你可能需要传递 wait_timeout 参数,像 Ghost 类文档中定义的那样(http://ghost-py.readthedocs.io/en/latest/#ghost.Ghost)。

对于 Scrapy,抓取器的异步特性和对特定的 URL 重试若干次的能力,让超时设置变成了一个略微难办的问题。当然,你可以在 Scrapy 设置中使用 DOWNLOAD_TIMEOUT(http://doc.scrapy.org/en/latest/topics/settings.html#download-timeout)直接改变超时时间。

如果你正在编写自己的 Python 脚本,并且使用 LXML 或 BeautifulSoup 来解析页面,添加超时到调用是你的职责。如果使用 requests 或 urllib2,你可以在调用页面的时候直接这样做。在 requests 中,你可以直接将其作为一个参数,添加到 get 请求中(http://docs.python-requests.org/en/latest/user/quickstart/#timeouts)。对于 urllib2,你需要传递超时时间,作为 urlopen 方法(https://docs.python.org/2/library/urllib2.html#urllib2.urlopen)的参数之一。

如果你正经历持续的网络方面的问题,并且脚本需要依据一个稳定的日程运行,建议你创建一些日志,尝试在另外一个网络上运行(即不是你的家庭网络,来判断你的家庭互联网连接是否存在问题),并且测试是否在一个非高峰时间段运行会有帮助。

 脚本在每天下午 5 点还是上午 5 点钟更新对你是否很重要?很可能在下午 5 点钟你的本地互联网服务提供商会非常繁忙,而上午 5 点钟可能会很安静。如果在高峰时段你的家庭网络很难做成事,那么很可能你的脚本也同样做不了什么!

除了网络问题之外,你可能会找到其他破坏抓取脚本的问题,比如互联网在不停变化这一事实。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文