思考“网络爬虫”时通常的想法:

• 通过网站域名获取 HTML 数据

• 根据目标信息解析数据

• 存储目标信息

• 如果有必要,移动到另一个网页重复这个过程


当网络浏览器遇到一个标签时,比如<img src="cuteKitten.jpg">,会向服务器发起另一个请求,以获取cuteKitten.jpg文件中的数据为用户充分渲染网页。但是,我们的Python程序没有返回并向服务器请求多个文件的逻辑,它只能读取我们已经请求的单个HTML文件。


1、初识urllib库

urllib是标准库,在 Python 3.x 里,urllib2 改名为 urllib,被分成一些子模块:urllib.request 、urllib.parse和urllib.error。

urlopen 用来打开并读取一个从网络获取的远程对象。

导入urlopen ,然后调用 html.read() 获取网页的HTML内容。

>>> from urllib.request import urlopen
>>> html = urlopen("http://pythonscraping.com/pages/page1.html")
>>> print(html.read())
b'<html>\n<head>\n<title>A Useful Page</title>\n</head>\n<body>\n<h1>An Interesting Title</h1>\n<div>\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n</div>\n</body>\n</html>\n'


2、使用虚拟环境

可以用虚拟环境保存库文件。

如下:创建了一个叫作scrapingEnv的新环境,然后激活它;在新建的scrapingEnv环境里,可以安装并使用BeautifulSoup;最后通过释放命令来退出环境。


$ virtualenv scrapingEnv

$ cd scrapingEnv/
$ source bin/activate

(scrapingEnv)ryan$ pip install beautifulsoup4
(scrapingEnv)ryan$ python
> from bs4 import BeautifulSoup
>

(scrapingEnv)ryan$ deactivate
$

可以使用如下任意一命令安装BeautifulSoup库

pip3 install bs4
pip3 install beautifulsoup4


3、初识BeautifulSoup库

把HTML内容传到BeautifulSoup对象(html.parser是内置的解析器),从网页中提取的 <h1> 标签被嵌在 BeautifulSoup 对象 bsObj 结构的第二层(html → body → h1)

>>> from urllib.request import urlopen
>>> from bs4 import BeautifulSoup
>>> html = urlopen("http://pythonscraping.com/pages/page1.html")
>>> bsObj = BeautifulSoup(html.read(), 'html.parser')
>>> print(bsObj.h1)
<h1>An Interesting Title</h1>

*下面的所有函数调用都可以产生同样的结果:

bsObj.h1

bsObj.html.body.h1

bsObj.body.h1

bsObj.html.h1


4、处理异常

网页在服务器上不存在(或者获取页面的时候出现错误), urlopen函数会抛出“HTTPError”异常。

如果服务器不存在(就是说链接打不开,或者是URL链接写错了),urlopen会返回一个None对象。

用以下方式处理:

try:
    html = urlopen("http://pythonscraping.com/pages/page1.html")
except HTTPError as e:
    print(e)
    # 中断程序,或者执行另一个方案
else:
    if html is None:
        print("URL is not found")
    else:
        # 程序继续
        pass


调用BeautifulSoup对象里的一个标签不存在会返回None对象。

再调用这个None对象下面的子标签,就会发生 AttributeError错误。

用以下方式处理:

try:
    bsObj = BeautifulSoup(html.read(), 'html.parser')
    badContent = bsObj.body.h2
except AttributeError as e:
    print("Tag was not found")
else:
    if badContent == None:
        print("Tag was not found")
    else:
        print(badContent)



5、重新整理组织以上代码

# -*- coding: utf-8 -*-
from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup


def getTitle(url):

    try:
        html = urlopen(url)        
    except HTTPError as e:
        return None
    try:
        bsObj = BeautifulSoup(html.read(), 'html.parser')
        title = bsObj.body.h1
    except AttributeError as e:
        return None
    return title
    
    
title = getTitle("http://pythonscraping.com/pages/page1.html")
if title == None:
    print("Title could not be found")
else:
    print(title)

我们创建了一个getTitle函数,用于返回网页的标题。如果获取网页的时候遇到问题就返回一个None对象。

在getTitle函数里面,我们像前面那样检查了HTTPError ,然后把两行BeautifulSoup代码封装在一个try语句里面。这两行中的任何一行有问题,AttributeError都可能被抛出(如果服务器不存在, html 就是一个None对象,html.read() 就会抛出 AttributeError )。