如何用 Python 实现 Web 抓取?

【编者按】本文作者为 Blog Bowl 联合创始人 Shaumik Daityari,主要介绍 Web 抓取技术的基本实现原理和方法。文章系国内 ITOM 管理平台 OneAPM 编译呈现,以下为正文。

如何用 Python 实现 Web 抓取?

随着电子商务的蓬勃发展,笔者近年越来越着迷于比价应用。我在网络上(甚至线下)的每次购买,都是在各大电商网站深入调研后的结果。

笔者常用的比价应用包括:RedLaser, ShopSavvy 以及 BuyHatke。这些应用有效提高了价格透明度,进而为消费者节省了可观的时间。

但是,你是否想过,这些应用如何得到那些重要数据?通常,它们会借助 Web 抓取技术来完成该任务。

Web 抓取的定义

Web 抓取是抽取网络数据的过程。只要借助合适的工具,任何你能看到的数据都可以进行抽取。在本文中,我们将重点介绍自动化抽取过程的程序,帮助你在较短时间内收集大量数据。除了笔者前文提到的用例,抓取技术的用途还包括:SEO 追踪、工作追踪、新闻分析以及笔者的最爱——社交媒体的情感分析!

一点提醒

在开启 Web 抓取的探险之前,请确保自己了解相关的法律问题。许多网站在其服务条款中明确禁止对其内容进行抓取。例如,Medium 网站就写道:“遵照网站 robots.txt 文件中的规定进行的爬取操作(Crawling)是可接受的,但是我们禁止抓取(Scraping)操作。”对不允许抓取的网站进行抓取可能会使你进入他们的黑名单!与任何工具一样,Web 抓取也可能用于复制网站内容之类的不良目的。此外,由 Web 抓取引起的法律诉讼也不在少数。

设置代码

在充分了解小心行事的必要之后,让我们开始学习 Web 抓取。其实,Web 抓取可以通过任何编程语言实现,在不久之前,我们使用 Node 实现过。在本文中,考虑到其简洁性与丰富的包支持,我们将使用 Python 实现抓取程序。

Web 抓取的基本过程

当你打开网络中的某个站点时,就会下载其 HTML 代码,由你的 web 浏览器对其进行分析与展示。该 HTML 代码包含了你所看到的所有信息。因此,通过分析 HTML 代码就能得到所需信息(比如价格)。你可以使用正则表达式在数据海洋中搜索你需要的信息,也可以使用函数库来解释 HTML,同样也能得到需要数据。

在 Python 中,我们将使用一个名为靓汤(Beautiful Soup)的模块对 HTML 数据进行分析。你可以借助 pip 之类的安装程序安装之,运行如下代码即可:

pip install beautifulsoup4

或者,你也可以根据源码进行构建。在该模块的文档说明页,可以看到详细的安装步骤。

安装完成之后,我们大致会遵循以下步骤实现 web 抓取:

  • 向 URL 发送请求
  • 接收响应
  • 分析响应以寻找所需数据

作为演示,我们将使用笔者的博客 http://dada.theblogbowl.in/. 作为目标 URL。

前两个步骤相对简单,可以这样完成:

from urllib import urlopen#Sending the http requestwebpage = urlopen('http://my_website.com/').read()

接下来,将响应传给之前安装的模块:

from bs4 import BeautifulSoup#making the soup! yummy ;)soup = BeautifulSoup(webpage, "html5lib")

请注意,此处我们选择了 html5lib 作为解析器。根据 BeautifulSoup 的文档,你也可以为其选择不同的解析器。

解析 HTML

在将 HTML 传给 BeautifulSoup 之后,我们可以尝试一些指令。譬如,检查 HTML 标记代码是否正确,可以验证该页面的标题(在 Python 解释器中):

>>> soup.title<title>Transcendental  Tech Talk</title>>>> soup.title.text
u'Transcendental  Tech Talk'
>>>

接下来,开始抽取页面中的特定元素。譬如,我想抽取博客中文章标题的列表。为此,我需要分析 HTML 的结构,这一点可以借助 Chrome 检查器完成。其他浏览器也提供了类似的工具。

如何用 Python 实现 Web 抓取?

使用 Chrome 检查器检查某个页面的 HTML 结构

如你所见,所有文章标题都带有 h3 标签与两个类属性:post-titleentry-title 类。因此,用 post-title 类搜索所有 h3 元素就能得到该页的文章标题列表。在此例中,我们使用 BeautifulSoup 提供的 find_all 函数,并通过 class_ 参数确定所需的类:

>>> titles = soup.find_all('h3', class_ = 'post-title') #Getting all titles>>> titles[0].textu'\nKolkata #BergerXP IndiBlogger meet, Marketing Insights, and some Blogging Tips\n'>>>

只通过 post-title 类进行条目搜索应该可以得到相同的结果:

>>> titles = soup.find_all(class_ = 'post-title') #Getting all items with class post-title>>> titles[0].textu'\nKolkata #BergerXP
   IndiBlogger meet, Marketing Insights, and some Blogging Tips\n'>>>

如果你想进一步了解条目所指的链接,可以运行下面的代码:

>>> for title in titles:...     # Each title is in the form of <h3 ...><a href=...>Post Title<a/></h3>...     print title.find("a").get("href")...http://dada.theblogbowl.in/2015/09/kolkata-bergerxp-indiblogger-meet.html
http://dada.theblogbowl.in/2015/09/i-got-published.html
http://dada.theblogbowl.in/2014/12/how-to-use-requestput-or-requestdelete.html
http://dada.theblogbowl.in/2014/12/zico-isl-and-atk.html...>>>

BeautifulSoup 内置了许多方法,可以帮助你玩转 HTML。其中一些方法列举如下:

>>> titles[0].contents
[u'\n', <a href="http://dada.theblogbowl.in/2015/09/kolkata-bergerxp-indiblogger-meet.html">Kolkata #BergerXP IndiBlogger meet, Marketing Insights, and some Blogging Tips</a>, u'\n']>>>

请注意,你也可以使用 children 属性,不过它有点像生成器

>>> titles[0].parent<div class="post hentry uncustomized-post-template">\n<a name="6501973351448547458"></a>\n<h3 class="post-title entry-title">\n<a href="http://dada.theblogbowl.in/2015/09/kolkata-bergerxp-indiblogger-meet.html">Kolkata #BergerXP IndiBlogger ...
>>>

你也可以使用正则表达式搜索 CSS 类,对此,本文档有详细的介绍

使用 Mechanize 模拟登录

目前为止,我们做的只是下载一个页面进而分析其内容。然而,web 开发者可能屏蔽了非浏览器发出的请求,或者有些网站内容只能在登录之后读取。那么,我们该如何处理这些情况呢?

对于第一种情况,我们需要在向页面发送请求时模拟一个浏览器。每个 HTTP 请求都包含一些相关的数据头(header),其中包含了访客浏览器、操作系统以及屏幕大小之类的信息。我们可以改变这些数据头,伪装为浏览器发送请求。

至于第二种情况,为了访问带有访客限制的内容,我们需要登录该网站,使用 cookie 保持会话。下面,让我们来看看在伪装成浏览器的同时,如何完成这一点。

我们将借助 cookielib 模块使用 cookie 管理会话。此外,我们还将用到 mechanize,后者可以使用 pip 之类的安装程序进行安装。

我们会通过 Blog Bowl 这个页面进行登录,并访问通知页面。下面的代码通过行内注释进行了解释:

import mechanize
import cookielib

from urllib import urlopen
from bs4 import BeautifulSoup# Cookie Jarcj = cookielib.LWPCookieJar()

browser = mechanize.Browser()
browser.set_cookiejar(cj)
browser.set_handle_robots(False)
browser.set_handle_redirect(True)# Solving issue #1 by emulating a browser by adding HTTP headersbrowser.addheaders = [('User-agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1')]# Open Login Pagebrowser.open("http://theblogbowl.in/login/")# Select Login form (1st form of the page)browser.select_form(nr = 0)# Alternate syntax - browser.select_form(name = "form_name")# The first <input> tag of the form is a CSRF token# Setting the 2nd and 3rd tags to email and passwordbrowser.form.set_value("email@example.com", nr=1)
browser.form.set_value("password", nr=2)# Logging inresponse = browser.submit()# Opening new page after loginsoup = BeautifulSoup(browser.open('http://theblogbowl.in/notifications/').read(), "html5lib")
如何用 Python 实现 Web 抓取?

通知页面的结构

# Print notificationsprint soup.find(class_ = "search_results").text
如何用 Python 实现 Web 抓取?

登录进通知页面后的结果

结语

许多开发者会告诉你:你在网络上看到的任何信息都可以被抓取。通过这篇文章,你学会了如何轻松抽取登录后才能看到的内容。此外,如果你的 IP 遭到了屏蔽,你可以掩盖自己的 IP 地址(或选用其他地址)。同时,为了看起来像是人类在访问,你应该在请求之间保留一定的时间间隔。

随着人们对数据的需求不断增长,web 抓取(不论原因好坏)技术在未来的应用只会更加广泛。也因此,理解其原理是相当重要的,不管你是为了有效利用该技术,还是为了免受其坑害。

OneAPM 能帮您查看 Python 应用程序的方方面面,不仅能够监控终端的用户体验,还能监控服务器性能,同时还支持追踪数据库、第三方 API 和 Web 服务器的各种问题。想阅读更多技术文章,请访问 OneAPM 官方技术博客

本文转自 OneAPM 官方博客

原文地址:https://www.sitepoint.com/web-scraping-for-beginners/

12: http://theblogbowl.in/notifications11]: http://theblogbowl.in/login/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,171评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,662评论 18 139
  • 作者:naughty原文:https://my.oschina.net/taogang/blog/271060本文...
    罗义的夏天阅读 1,283评论 0 1
  • 今天在“途正学习群里”,偶然遇见一位小姑娘,看到她的发问仿佛看见了两年前的自己,曾经的我也一度怀疑,我学的专业到底...
    小葡葡萄儿阅读 470评论 0 0
  • 昨晚冬令时转夏令时,一早起来看了看手机,心想,我又亏了一个小时,笑。 巴黎的春似乎已经来到,春寒料峭,但久违的灿烂...
    voutetoilee阅读 178评论 0 0