《Python网络数据采集》第1章 初见网络爬虫

目录

1.1 网络连接

1.2 BeautifulSoup简介

1.2.1 安装BeautifulSoup 

1.2.2 运行BeautifulSoup

 1.2.3 可靠的网络连接


1.1 网络连接

的确,网络浏览器是一个非常有用的应用,它创建信息的数据包,发送它们,然后把你获取的数据解释成漂亮的图像、声音、视频和文字。但是,网络浏览器就是代码,而代码是可以分解的,可以分解成许多基本组件,可重写、重用,以及做成我们想要的任何东西。网络浏览器可以让服务器发送一些数据,到那些对接无线(或有线)网络接口的应用上,但是许多语言也都有实现这些功能的库文件。
让我们看看 Python 是如何实现的:
from urllib.request import urlopen
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
print(html.read())
这将会输出http://www.baidu.com这个网页的全部HTML代码。 更准确地说,这会输出在域名为http://www.baidu.com 的服务器上 < 网络应用根地址 >/ pages 文件夹里的 HTML 文件 page1.html 的源代码。
有什么区别?现在大多数网页需要加载许多相关的资源文件。可能是图像文件、JavaScript文件、 CSS 文件,或你需要连接的其他各种网页内容。当网络浏览器遇到一个标签时,比如 ,会向服务器发起另一个请求,以获取 cuteKitten.jpg 文件中的数据为用户充分渲染网页。但是,我们的 Python 程序没有返回并向服务器请求多个文件的逻辑,它只能读取我们已经请求的单个 HTML 文件。
那么我们应该怎样做呢?幸好 Python 语法接近正常英文,下面这行代码
from urllib.request import urlopen
其实已经显示了它的含义:它查找 Python request 模块(在 urllib 库里面),只导入一个urlopen 函数。
urllib Python 的标准库(就是说你不用额外安装就可以运行这个例子),包含了从网络请求数据,处理 cookie,甚至改变像请求头和用户代理这些元数据的函数。我们将在本书中广泛使用 urllib ,所以建议你读读这个库的 Python 文档(https://docs.python.org/3/library/urllib.html )。
urlopen 用来打开并读取一个从网络获取的远程对象。因为它是一个非常通用的库(它可以轻松读取 HTML 文件、图像文件,或其他任何文件流),所以我们将在本书中频繁地使用它。

1.2 BeautifulSoup简介

“美味的汤,绿色的浓汤,
在热气腾腾的盖碗里装!
谁不愿意尝一尝,这样的好汤?
晚餐用的汤,美味的汤!”

 BeautifulSoup 库的名字取自刘易斯·卡罗尔在《爱丽丝梦游仙境》里的同名诗歌。在故事中,这首诗是素甲鱼唱的。

就像它在仙境中的说法一样,BeautifulSoup 尝试化平淡为神奇。它通过定位 HTML 标签来格式化和组织复杂的网络信息,用简单易用的 Python 对象为我们展现 XML 结构信息。

1.2.1 安装BeautifulSoup 

由于 BeautifulSoup 库不是 Python 标准库,因此需要单独安装。在本书中,我们将使用最新的 BeautifulSoup 4 版本(也叫 BS4 )。 BeautifulSoup 4 的所有安装方法都在 http://www.crummy.com/software/BeautifulSoup/bs4/doc/ 里面。
可以直接在cmd中使用如下代码进行安装
pip3 install beautifulsoup4

1.2.2 运行BeautifulSoup

BeautifulSoup 库最常用的对象恰好就是 BeautifulSoup 对象。让我们把本章开头的例子调整一下运行看看:
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
bs0bj = BeautifulSoup(html.read())
print(bs0bj.h1)
输出结果是:

An Interesting Title

和前面例子一样,我们导入 urlopen ,然后调用 html.read() 获取网页的 HTML 内容。这样就可以把 HTML 内容传到 BeautifulSoup 对象,转换成下面的结构:
html → ......
 — head → A Useful Page<title></head>
 — title → <title>A Useful Page
 — body → 

An Int...

Lorem ip...
— h1 →

An Interesting Title

— div →
Lorem Ipsum dolor...
可以看出,我们从网页中提取的

标签被嵌在 BeautifulSoup 对象 bsObj 结构的第二层( html body h1 )。但是,当我们从对象里提取 h1 标签的时候,可以直接调用它:bs0bj.h1

希望这个例子可以向你展示 BeautifulSoup 库的强大与简单。其实,任何 HTML(或XML)文件的任意节点信息都可以被提取出来,只要目标信息的旁边或附近有标记就行。
在第 3 章,我们将进一步探讨一些更复杂的 BeautifulSoup 函数,还会介绍正则表达式,以及如何把正则表达式用于 BeautifulSoup 以对网站信息进行提取。

 1.2.3 可靠的网络连接

网络是十分复杂的。网页数据格式不友好,网站服务器宕机,目标数据的标签找不到,都是很麻烦的事情。网络数据采集最痛苦的遭遇之一,就是爬虫运行的时候你洗洗睡了,梦想着明天一早数据就都会采集好放在数据库里,结果第二天醒来,你看到的却是一个因某种数据格式异常导致运行错误的爬虫,在前一天当你不再盯着屏幕去睡觉之后,没过一会儿爬虫就不再运行了。那个时候,你可能想骂发明互联网(以及那些奇葩的网络数据格式)的人,但是你真正应该斥责的人是你自己,为什么一开始不估计可能会出现的异常!
让我们看看爬虫 import 语句后面的第一行代码,如何处理那里可能出现的异常:
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
这行代码主要可能会发生两种异常:
1.网页在服务器上不存在(或者获取页面的时候出现错误)
2. 服务器不存在
第一种异常发生时,程序会返回 HTTP 错误。 HTTP 错误可能是“ 404 Page Not Found ”“500Internal Server Error ”等。所有类似情形, urlopen 函数都会抛出“ HTTPError”异常。我们可以用下面的方式处理这种异常:
try:
    html = urlopen("http://www.pythonscraping.com/pages/page1.html")
except HTTPError as e:
    print(e)
    # 返回空值,中断程序,或者执行另一个方案
else:
    # 程序继续。注意:如果你已经在上面异常捕捉那一段代码里返回或中断(break),
    # 那么就不需要使用else语句了,这段代码也不会执行
如果程序返回 HTTP 错误代码,程序就会显示错误内容,不再执行 else 语句后面的代码。
如果服务器不存在(就是说链接 http://www.pythonscraping.com/ 打不开,或者是 URL 链接写错了), urlopen 会返回一个 None 对象。这个对象与其他编程语言中的 null 类似。我们可以增加一个判断语句检测返回的 html 是不是 None
if html is None:
    print("URL is not found")
else:
    # 程序继续
当然,即使网页已经从服务器成功获取,如果网页上的内容并非完全是我们期望的那样,仍然可能会出现异常。每当你调用 BeautifulSoup 对象里的一个标签时,增加一个检查条件保证标签确实存在是很聪明的做法。如果你想要调用的标签不存在, BeautifulSoup 就会返   None 对象。不过,如果再调用这个 None 对象下面的子标签,就会发生 AttributeError 误。

下面这行代码(nonExistentTag 是虚拟的标签,BeautifulSoup 对象里实际没有) 

print(bsObj.nonExistentTag)
会返回一个 None 对象。处理和检查这个对象是十分必要的。如果你不检查,直接调用这个None 对象的子标签,麻烦就来了。如下所示。
print(bsObj.nonExistentTag.someTag)
这时就会返回一个异常:
AttributeError: 'NoneType' object has no attribute 'someTag'
那么我们怎么才能避免这两种情形的异常呢?最简单的方式就是对两种情形进行检查:
try:
     badContent = bsObj.nonExistingTag.anotherTag
except AttributeError as e:
     print("Tag was not found")
else:
     if badContent == None:
         print ("Tag was not found")
     else:
         print(badContent)
初看这些检查与错误处理的代码会觉得有点儿累赘,但是,我们可以重新简单组织一下代码,让它变得不那么难写(更重要的是,不那么难读)。例如,下面的代码是上面爬虫的另一种写法:
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())
            title = bsObj.body.h1
        except AttributeError as e:
            return None
        return title
title = getTitle("http://www.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 )。其实,我们可以在 try 语句里面放任意多行代码,或者放一个在任意位置都可以抛出 AttributeError 的函数。
在写爬虫的时候,思考代码的总体格局,让代码既可以捕捉异常又容易阅读,这是很重要的。如果你还希望能够很大程度地重用代码,那么拥有像 getSiteHTML getTitle 这样的通用函数(具有周密的异常处理功能)会让快速稳定地网络数据采集变得简单易行。

你可能感兴趣的:(爬虫,网络,爬虫,python)