用Python实现网络爬虫

Web Scraping with Python

这是《Web Scraping with Python》一书的阅读笔记。该笔记跳过了一些不必要的描述,对书的代码也做了核实,也引入了一些我自己对爬虫脚本实现的理解。

第一章 你的第一个网络爬虫程序

为了帮助理解,作者用了一个例子。假设Alice有一个网络服务器。而Bob想用一个台式机通过浏览器访问Alice的服务器上运行的某个网站。整个访问过程归纳如下:

1. Bob输入访问网站的地址后,Bob的电脑传输一段二进制的数据,这些数据包含数据头数据内容。数据头包含发送方的mac地址和目的地的ip地址,而数据内容包含了针对Alice网络服务器的请求,例如,获得某个网页页面。

2. Bob的本地网络路由器将数据打包传输到Alice的ip地址。

3. Bob的数据最后通过物理电缆进行传输。

4. Alice的服务器接受到了Bob的数据包。

5. Alice的服务器识别存于数据头的端口号,发现是80,意味着这是一个网页请求,于是调用网页服务器相关的程序。

6. 网页服务器程序接受到如下信息::

- This is a GET request

- The following file is requested: index.html

7. 网页服务器程序载入正确的HTML 文件,并打包通过本地路由发送给Bob的电脑.

而Python的库包含了模拟浏览器访问某个页面的功能,如下:

from urllib.request import urlopen

html = urlopen("http://pythonscraping.com/pages/page1.html")

print(html.read())

这是一段Python3的程序,请用Python3.X的版本运行它。执行后,该网页的HTML内容会被打印出来,其实就是Chrome浏览器右键查看网页源代码可以看到的网页内容。

urllib 还是 urllib2?

Python2中用urllib2,而Python3中用urllib。urllib是Python的标准库,用于网页数据请求,处理Cookies,甚至更改请求者的数据头信息。因为正本书的代码都会涉及urllib的使用,所以有空的时候可以看看Python的官方文档:https://docs.python.org/3/library/urllib.html

BeautifulSoup的介绍和安装

一句话来概括,BeautifulSoup将不可能变成了可能,它将HTML的内容组织成了Python可以识别的对象格式。因为BeautifulSoup不是Python默认的库,我们要先安装它。本书用BeautifulSoup的第四个版本。下载地址:https://pypi.python.org/pypi/beautifulsoup4。可下载安装包:beautifulsoup4-4.5.3.tar.gz(md5)。解压后使用命令:"python.exe setup.py install" 进行安装,这种安装方式在Windows下也可行。当然也可以使用pip命令安装,省去下载安装包的过程:"pip install beautifulsoup4",但Windows下,要另外装pip工具。

BeautifulSoup初体验

from urllib.request import urlopen

from bs4 import BeautifulSoup

html = urlopen("http://www.pythonscraping.com/exercises/exercise1.html")

bsObj = BeautifulSoup(html.read(), "html.parser");

print(bsObj.h1)

这段代码解析了exercise1.html这个HTML文件,并输出了h1这个字段的内容:

<h1>An Interesting Title<h1>

HTML文件的结构

上面这个图显示的是HTML的常用结构。bsObj.h1是一个快捷的访问h1数据的方法,实际上类似这样的访问也是有效的:bsObj.html.body.h1,bsObj.body.h1,bsObj.html.h1

通过这个例子,我们应该可以体会到BeautifulSoup的方便。第三章将对BeautifulSoup做更深入的讨论,例如:使用正则表达式提取网页数据。

考虑脚本的稳定性

try:

        html = urlopen("http://www.pythonscraping.com/exercises/exercise1.html")

except HTTPError as e:

        print(e)

        #return null, break, or do some other "Plan B"

else:

        #program continues. Note: If you return or break in the

       #exception catch, you do not need to use the "else" statement

考虑到某些情况下会出现页面无法访问的问题,建议加上以上出错判断的代码。如果尝试访问的BeautifulSoup标签不存在,BeautifulSoup会返回None对象。那么问题来了,访问None对象会抛出AttributeError异常。以下是一个鲁棒的获取数据的脚本:

容错的脚本例子

第二章 HTML解析的进阶

第一章介绍的BeautifulSoup可以很方便地提取需要的带标签的数据,但随着标记深度的递增,简单地使用对象数据不利于表达和调试,例如以下代码就很难理解:

bsObj.findAll("table")[4].findAll("tr")[2].find("td").findAll("div")[1].find("a")

这段代码不仅不美观,还不够鲁棒。当爬取的网站做了小幅度的更改后,这段代码就无效了。有没有更好的方法呢?

BeautifulSoup的另一个功能

通常,一个HTML页面都包含有CSS样式,例如

<span class="green"></span>

<span class="red"></span>

BeautifulSoup可以通过制定class的值,过滤一些不需要的内容。例如

nameList=bsObj.findAll("span", {"class":"green"})

for name in nameList:

print(name.get_text())

这句代码可以获得class为green的span内容。其中函数get_text()可以获得标签的内容。

findAll和find的函数定义如下

findAll(tag,attributes,recursive,text,limit,keywords)

find(tag,attributes,recursive,text,keywords)

findAll还有很多有用的写法

.findAll({"h1","h2","h3","h4","h5","h6"})

.findAll("span", {"class":"green","class":"red"})

这些代码可以列出所有有关的标签内容。recursive设置为true的话就执行递归查询。默认情况该值为true。

nameList=bsObj.findAll(text="the prince")

print(len(nameList))

以上的代码可以统计"the prince"字符串出现的次数,输出为7。(真是太强大了)

allText=bsObj.findAll(id="text") #和bsObj.findAll("", {"id":"text"})是等价的

print(allText[0].get_text())

这段代码可以根据attribute来选择内容,代码的输出是id是text的div包含的所有文本内容。因为class是Python的关键字,bsObj.findAll(class="green")是不允许的,可以用以下代码替换:

bsObj.findAll(class_="green")

bsObj.findAll("", {"class":"green"}

正则表达式

正则表达式在很多人眼里都是个高大上的工具,什么都不多说了,先来一个例子。

aa*bbbbb(cc)*(d | )

aa*

这里的*指代任何东西

bbbbb

代表5个b,没有其它的意义

(cc)*

代表任意个重复的c,也可以是0个

(d | )

|代表或的意思,这句代表以d和空格结尾,或者仅仅以空格结尾

一些通用的规则如下,例如E-mail的命名规则:

E-mail能包含的字符为:大小写字母、数字、点、加号或者下划线,并且每个E-mail都要包含@符号。这个规则用正则表达式可以这样写:[A-Za-z0-9\._+]+

正则表达式非常得智能,它会知道中括号中的内容是指从A到Z的字符都可以,\.代表一个点(period),然后最后的+号以为着这些字符可以出现任意多次,但至少要出现一次。

再看一个更复杂的:[A-Za-z0-9\._+]+@[A-Za-z]+\.(com|org|edu|net),这种正则表达式就可以匹配任意以com|org|edu|net结尾的邮箱地址。

接下来详细介绍12个在Python中常用的正则表达式

.代表任意一个字符,如果是要查找点的话,就用转义字符\.

+的作用是将前面一个字符或一个子表达式重复一遍或者多遍。

*跟在其他符号后面表达可以匹配到它0次或多次,例如https*就可以找出http://和https://两种。

这里举一个例子介绍正则表达式在实际数据抓取,原书的代码编译不过,我做了一些修改。

from urllib.request import urlopen

from bs4 import BeautifulSoup

import re

html = urlopen("http://www.pythonscraping.com/pages/page3.html")

bsObj = BeautifulSoup(html.read(), "html.parser")

images = bsObj.findAll("img", {"src":re.compile("\.\.\/img\/gifts\/img.*\.jpg")})

for image in images:

print(image["src"])


访问属性

通过这样的方式就可以访问某个属性:myImgTag.attrs['src']

Lambda表达式

作者针对Lambda的描述是某个函数的参数是另一个函数。我的理解是,某个查找的条件是某个判断函数的返回值。例如:

bsObj.findAll(lambda tag: len(tag.attrs) == 2)

这句代码可以找出tag有两个的条目。返回的是len(tag.attrs) == 2为True的所有条目。

感觉这两章的内容足够应付基本的爬虫应用了,以后有额外的需求,再解读其他几章。^__^

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

推荐阅读更多精彩内容