用Python实现网络爬虫

作者: 浮云匿晨晖 | 来源:发表于2017-01-09 17:00 被阅读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的所有条目。

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

    相关文章

      网友评论

        本文标题:用Python实现网络爬虫

        本文链接:https://www.haomeiwen.com/subject/srdhbttx.html