Python语言与信息数据获取和机器学习

用Python实现网络爬虫

2017-01-09  本文已影响540人  浮云匿晨晖
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的所有条目。

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

上一篇下一篇

猜你喜欢

热点阅读