我心中的王者:Python-第21章 网络爬虫

我心中的王者:Python-第21章 网络爬虫

过去我们浏览网页是使用浏览器,例如,Microsoft公司的Internet Explorer、Google公司的Chrome、Apple公司的Safari等。现在学了Python,我们可以不再需要通过浏览器浏览网页了,除了浏览网页,本章笔者也将讲解如何从网站下载有用的信息。

一般我们将从网络搜寻资源的程序称之为网络爬虫,一些著名的搜索引擎公司就是不断地送出网络爬虫搜寻网络最新信息,以保持搜索引擎的热度。

21-1 上网不再需要浏览器了

这一节将介绍webbrowser模块浏览网页,在程序前方需导入此模块。

 import webbrowser

Python有提供webbrowser模块,可以调用这个模块的open( )方法,就可以打开指定的网页了。

程序实例ch21_1.py:打开上奇信息公司(http://www.grandtech.info)网页。

# ch21_1.py
import webbrowser
webbrowser.open('http://www.grandtech.info')

执行结果 请留意以下浏览程序外观,不属于目前各位已知的浏览器。
在这里插入图片描述

21-2 下载网页信息使用requests模块

requests是第三方模块,读者需参考附录B,使用下列指令下载此模块。

 pip install requests

21-2-1 下载网页使用requests.get( )方法

requests.get( )方法内需放置欲下载网页信息的网址当参数,这个方法可以传回网页的HTML源文件。

程序实例ch21_2.py:下载上奇信息网页内容做测试,这个程序会列出返回值的数据类型。

# ch21_2.py
import requests

url = 'http://www.baidu.com'
htmlfile = requests.get(url)
print(type(htmlfile))



执行结果

<class 'requests.models.Response'>

由上述可以知道使用requests.get( )之后传回的数据类型是Response对象。

21-2-2 认识Response对象

Response对象内有下列几个重要属性:

status_code:如果值是requests.codes.ok,表示获得的网页内容成功。

text:网页内容。

程序实例ch21_3.py:检查ch21_2.py获得的网页内容是否成功。

# ch21_3.py
import requests

url = 'http://www.baidu.com'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    print("取得网页内容成功")
else:
    print("取得网页内容失败")

执行结果

取得网页内容成功

程序实例ch21_4.py:扩充ch21_3.py,取得网页内容大小。

# ch21_4.py
import requests

url = 'http://www.baidu.com'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    print("取得网页内容成功")
else:
    print("取得网页内容失败")
print("网页内容大小 = ", len(htmlfile.text))

执行结果

取得网页内容成功
网页内容大小 =  2381

程序实例ch21_5.py:打印网页的原始码,然后可以看到密密麻麻的网页内容(繁体中文)。

# ch21_5.py
import requests

url = 'http://www.baidu.com'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    print("取得网页内容成功")
else:
    print("取得网页内容失败")
print(htmlfile.text)            # 打印网页内容

执行结果

取得网页内容成功
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>ç¾åº¦ä¸ä¸ï¼ä½ å°±ç¥é</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=ç¾åº¦ä¸ä¸ class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>æ°é»</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>å°å¾</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>è§é¢</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>è´´å§</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>ç»å½</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">ç»å½</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">æ´å¤äº§å</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>å³äºç¾åº¦</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使ç¨ç¾åº¦åå¿è¯»</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>æè§åé¦</a>&nbsp;京ICPè¯030173å·&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

21-2-3 搜索页特定内容

继续先前的内容,网页内容下载后,如果我们想要搜寻特定字符串,可以使用许多方法,下列将简单地用2个方法处理。

程序实例ch21_6.py:搜寻字符串“荷兰”使用方法1,使用方法2不仅搜寻,如果找到同时列出执行结果。这个程序执行时,如果网页内容下载成功,会要求输入欲搜寻的字符串,将此字符串放入pattern变量。使用2种方法搜寻,方法1会列出搜寻成功或失败,方法2会列出搜寻到此字符串的次数。

# ch21_6.py
import requests
import re

url = 'https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9621344775873013540%22%7D&n_type=-1&p_from=-1'
htmlfile = requests.get(url)
if htmlfile.status_code == requests.codes.ok:
    pattern = input("请输入欲搜寻的字符串 : ")    # pattern存放欲搜寻的字符串
# 使用方法1
    if pattern in htmlfile.text:                # 方法1
        print("搜寻 %s 成功" % pattern)
    else:
        print("搜寻 %s 失败" % pattern)
    # 使用方法2, 如果找到放在列表name内
    name = re.findall(pattern, htmlfile.text)   # 方法2
    if name != None:
        print("%s 出现 %d 次" % (pattern, len(name)))
    else:
        print("%s 出现 0 次" % pattern)
else:
    print("网页下载失败")


执行结果

请输入欲搜寻的字符串 : 荷兰
搜寻 荷兰 成功
荷兰 出现 22

21-2-4 下载网页失败的异常处理

有时候我们输入网址错误或有些网页有反爬虫机制,造成下载网页失败,其实建议可以使用第15章程式除错与异常处理观念处理这类问题。Response对象有raise_for_status( ),可以针对网址正确但是后续文件名错误的状况产生异常处理。下列将直接以实例解说。

程序实例ch21_7.py:下载网页错误的异常处理,由于不存在file_not_existed造成这个程序异常发生。

# ch21_7.py
import requests

url = 'http://www.grandtech.info/file_not_existed'  # 不存在的内容
htmlfile = requests.get(url)
try:
    htmlfile.raise_for_status()                 # 异常处理
    print("下载成功")
except Exception as err:                        # err是系统自定义的错误讯息
    print("网页下载失败: %s" % err)

执行结果
在这里插入图片描述

若是忘记了try:的用法可参考第15章,若是忘记第9行用法可以参考15-2-4小节。上述raise_for_status( )可以处理网址正确但是后面附加文件错误的问题,可是无法处理网址错误的信息。

程序实例ch21_8.py:一个错误的网址造成出现一长串的错误。

# ch21_8.py
import requests

url = 'http://www.gzaxxc.com/file_not_existed'  # 错误的网址
htmlfile = requests.get(url)
try:
    htmlfile.raise_for_status()                 # 异常处理
    print("下载成功")
except Exception as err:                        # err是系统自定义的错误讯息
    print("网页下载失败: %s" % err)

执行结果
在这里插入图片描述

很明显执行异常处理期间又产生了异常,所以程序错误产生中断,有时候可以将requests.get( )放在try:后面。

程序实例ch21_9.py:重新设计下载网页错误的异常处理。

# ch21_9.py
import requests

url = 'http://www.gzaxxc.com/file_not_existed'  # 错误的网址
try:
    htmlfile = requests.get(url)
    print("下载成功")
except Exception as err:                        # err是系统自定义的错误讯息
    print("网页下载失败: %s" % err)

执行结果
在这里插入图片描述

从上述可以看到,即使网址错误,程序还是依照我们设计的逻辑执行。

21-2-5 网页服务器阻挡造成读取错误

现在有些网页也许基于安全原因,或是不想让太多网络爬虫造访造成网络流量增加,因此会设计程序阻挡网络爬虫提取信息,碰上这类问题就会产生406的错误,如下所示:

程序实例ch21_9_1.py:网页服务器阻挡造成编号406的错误,无法提取网页信息。

# ch21_9_1.py
import requests

url = 'http://aaa.24ht.com.tw/'
htmlfile = requests.get(url)
htmlfile.raise_for_status()

执行结果
在这里插入图片描述

上述程序第6行的raise_for_status( )主要是如果Response对象htmlfile在前一行提取网页内容有错误码时,将列出错误原因,406错误就是网页服务器阻挡。用这行程序代码,可以快速中断协助我们侦错程序的错误。

21-2-6 爬虫程序伪装成浏览器

其实我们使用requests.get( )方法到网络上读取网页数据,这类的程序就称网络爬虫程序,甚至你也可以将各大公司所设计的搜索引擎称为网络爬虫程序。为了解决爬虫程序被服务器阻挡的困扰,我们可以将所设计的爬虫程序伪装成浏览器,方法是在程序前端加上headers内容。

程序实例ch21_9_2.py:使用伪装浏览器方式,重新设计ch21_9_1.py。

# ch21_9_2.py
import requests

headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64)\
            AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101\
            Safari/537.36', }
url = 'http://aaa.24ht.com.tw/'
htmlfile = requests.get(url, headers=headers)
htmlfile.raise_for_status()
print("儰装浏览器撷取网络数据成功")

执行结果

儰装浏览器撷取网络数据成功

上述的重点是第4-6行的叙述,其实这是一个标题(headers)定义,第4和5行末端的反斜杠“\”主要表达下一行与这一行是相同叙述,也就是处理同一叙述太长时分行撰写,Python会将4-6行视为同一叙述。然后第8行调用requests.get( )时,第2个参数需要加上“headers=headers”,这样这个程序就可以伪装成浏览器,顺利取得网页数据了。

其实将Pythont程序伪装成浏览器比想象的复杂,上述headers定义碰上安全机制强大的网页也可能失效,更详细的解说超出本书范围。

21-2-7 存储下载的网页

使用requests.get( )获得网页内容时,是存储在Response对象类型内,如果要将这类型的对象存入硬盘内,需使用Response对象的iter_content( )方法,这个方法是采用重复迭代方式将Response对象内容写入指定的文件内,每次写入指定扇区大小是以Bytes为单位,一般可以设定1024×5或1024×10或更多。

程序实例ch21_10.py:下载深石数字公司网页,同时将网页内容存入out21_10.txt文件内。

# ch21_10.py
import requests

url = 'http://www.baidu.com'                     # 网址
try:
    htmlfile = requests.get(url)
    print("下载成功")
except Exception as err:                                # err是系统自定义的错误讯息
    print("网页下载失败: %s" % err)
# 储存网页内容
fn = 'out21_10.txt'
with open(fn, 'wb') as file_Obj:                        # 以二进制储存
    for diskStorage in htmlfile.iter_content(10240):    # Response对象处理
        size = file_Obj.write(diskStorage)              # Response物件写入
        print(size)                                     # 列出每次写入大小
    print("以 %s 储存网页HTML档案成功" % fn)


执行结果

下载成功
2381
以 out21_10.txt 储存网页HTML档案成功

由于这个网页文件内容比较小,所以笔者将每次写入文件大小设为10240bytes,程序第12行所打开的是以二进制可写入“wb”方式打开,这是怕网页内有Unicode码。程序第13~15行是一个循环,这个循环会将Response对象htmlfile以循环方式写入所打开的file_Obj,最后是存入第11行设定的out21_11.txt文件内。程序第14行每次使用write( )写入Response对象时会回传所写入网页内容的大小,所以第15行会列出当次循环所写入的大小。

21-3 检视网页原始文件

前一节笔者教导读者利用requests.get( )取得网页内容的原始HTML文件,其实也可以使用浏览器取得网页内容的原始文件。检视网页的原始文件目的不是要模仿设计相同的网页,主要是掌握几个关键重点,然后提取我们想要的数据。

21-3-1 建议阅读书籍

21-3-2 以Microsoft浏览器为实例

Microsoft浏览器Internet Explorer简称IE,此例是使用IE打开清华大学出版社网页,在网页内单击鼠标右键,出现快捷菜单时,执行查看源指令。
在这里插入图片描述

就可以看到此网页的原始HTML文件。
在这里插入图片描述

如果使用的是Chrome浏览器,将鼠标光标放在网页上单击鼠标右键,打开快捷菜单,再执行View page source指令,也可以打开新窗口显示此网页的HTML原始文件。

21-3-3 源文件的重点

假设你想要下载某网页的图片,可以进入网页了解此网页的结构,例如,如果我们想要下载台湾彩劵公司的威力彩开奖号码,我们可以先进入此公司网页。

将鼠标光标移至威力彩开奖结果,单击鼠标右键,出现快捷菜单,执行查看源指令。接着出现HTML源文件的窗口,请执行编辑/搜索,再输入1000085,这是笔者写本书时最新开奖期数,可以得到下列结果。
在这里插入图片描述

由上图我们已经找到放置威力彩劵号码球的地点了,接着我们必须了解此区域特性,然后再针对此,执行搜寻,最后设计可以找出彩劵号码的爬虫程序,可参考ch21_20.py。

如果我们现在要下载某个网页的所有图片文件,可以进入该网页,例如,想要下载上奇信息网页(http://www.grandtech.info)的图片文件,可以打开该网页的HTML文件,然后请执行编辑/寻找,再输入‘<img’,接着可以了解该网页图片文件的状况。
在这里插入图片描述

由上图可以看到图片文件是在images文件夹内,其实我们也可以使用“网址+文件路径”,列出图文件的内容。

在这里插入图片描述

21-4 解析网页使用BeautifulSoup模块

从前面章节读者应该已经了解了如何下载网页HTML源文件,也应该对网页的基本架构有基本认识,本节要介绍的是使用BeautifulSoup模块解析HTML文件。目前这个模块是第4版,模块名称是beautifulsoup4,可参考附录B,以下列方式安装:

 pip install beautifulsoup4

虽然安装是beautifulsoup4,但是导入模块时是用下列方式:

 import bs4

21-4-1 建立BeautifulSoup对象

可以使用下列语法建立BeautifulSoup对象。

 htmlFile = requests.get(‘http://www.grandtech.info') # 下载网页内容
 objSoup = bs4.BeautifulSoup(htmlFile.text, ‘lxml') # lxml是解析HTML文件方式

上述是以下载上奇信息网页为例,当网页下载后,将网页内容的Response对象传给bs4.BeautifulSoup( )方法,就可以建立BeautifulSoup对象。至于另一个参数“lxml”目的是注明解析HTML文件的方法,常用的有下列方法。

‘html.parser’:这是老旧的方法(3.2.3版本前),兼容性比较不好。

‘lxml’:速度快,兼容性佳,这是本书采用的方法。

‘html5lib’:速度比较慢,但是解析能力强,需另外安装html5lib。

pip install html5lib

程序实例ch21_11.py:解析http://www.baidu.com网页,主要是列出数据类型。

# ch21_11.py
import requests, bs4

htmlFile = requests.get('http://www.baidu.com')
objSoup = bs4.BeautifulSoup(htmlFile.text, 'lxml')
print("打印BeautifulSoup对象数据型态 ", type(objSoup))

执行结果

打印BeautifulSoup对象数据型态  <class 'bs4.BeautifulSoup'>

从上述我们获得了BeautifulSoup的数据类型了,表示我们获得初步成果了。

21-4-2 基本HTML文件解析——从简单开始

真实世界的网页是很复杂的,所以笔者想先从简单的HTML文件开始解析网页。在ch21_11.py程序第5行第一个参数htmlFile.text是网页内容的Response对象,我们可以在ch21文件夹放置一个简单的HTML文件,然后先学习使用BeautifulSoup解析此HTML文件。

程序实例myhtml.html:在ch21文件夹有myhtml.html文件,这个文件内容如下:

<!doctype html>
<html>
<head>
   <meta charset="utf-8">
   <title>晓波著作</title>
   <style>
      h1#author { width:400px; height:50px; text-align:center;
	     background:linear-gradient(to right,yellow,green);
      }
	  h1#content { width:400px; height:50px;
		 background:linear-gradient(to right,yellow,red); 
      }
      section { background:linear-gradient(to right bottom,yellow,gray); }
   </style>
</head>
<body>
<h1 id="author">晓波</h1>
<img src="hung.jpg" width="100">
<section>
   <h1 id="content">一个人的极境旅行 - 南极大陆北极海</h1>
   <p>2015/2016<strong>晓波</strong>一个人到南极</p>
   <img src="travel.jpg" width="300"
</section>
<section>
   <h1 id="content">HTML5+CSS3王者归来</h1>
   <p>本书讲解网页设计使用HTML5+CSS3</p>
   <img src="html5.jpg" width="300">
</section>
</body>
</html>

本节有几个小节将会解析此份HTML文件。

程序实例ch21_12.py:解析本书ch21文件夹的myhtml.html文件,列出对象类型。

# ch21_12.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
print("打印BeautifulSoup对象数据型态 ", type(objSoup))

执行结果

打印BeautifulSoup对象数据型态  <class 'bs4.BeautifulSoup'>

上述可以看到解析ch21文件夹的myhtml.html文件是初步成功的。

21-4-3 页标题title属性

BeautifulSoup对象的title属性可以传回页标题的标签内容。

程序实例ch21_13.py:使用title属性解析myhtml.html文件的页标题,本程序会列出对象类型与内容。

# ch21_13.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
print("对象类型  = ", type(objSoup.title))
print("打印title = ", objSoup.title)

执行结果

对象类型  =  <class 'bs4.element.Tag'>
打印title =  <title>晓波著作</title>

从上述执行结果可以看到所解析的objSoup.title是一个HTML卷标对象。

21-4-4 去除卷标传回文字text属性

前一节实例的确解析了myhtml.html文件,传回解析的结果是一个HTML的标签,不过我们可以使用text属性获得此卷标的内容。

程序实例ch21_14.py:扩充ch21_13.py,列出解析的标签内容。

# ch21_14.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
print("打印title = ", objSoup.title)
print("title内容 = ", objSoup.title.text)

执行结果

打印title =  <title>晓波著作</title>
title内容 =  晓波著作

21-4-5 传回所找寻的第一个符合的标签find( )

这个函数可以找寻HTML文件内第一个符合的标签内容,例如,find(‘h1’)是要找第一个h1的标签。如果找到了就传回该卷标字符串,我们可以使用text属性获得内容,如果没找到就传回None。

程序实例ch21_15.py:传回第一个

标签。

# ch21_15.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find('h1')
print("数据型态= ", type(objTag))
print("打印Tag = ", objTag)
print("Tag内容 = ", objTag.text)

执行结果

数据型态=  <class 'bs4.element.Tag'>
打印Tag =  <h1 id="author">晓波</h1>
Tag内容 =  晓波

21-4-6 传回所找寻的所有符合的标签find all( )

这个函数可以找寻HTML文件内所有符合的标签内容,例如,find_all(‘h1’)是要找所有h1的标签。如果找到了就传回该标签列表,如果没找到就传回空列表。

程序实例ch21_16.py:传回所有的

标签。

# ch21_16.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find_all('h1')
print("数据型态    = ", type(objTag))     # 打印数据型态
print("打印Tag列表 = ", objTag)           # 打印列表
print("以下是打印列表元素 : ")
for data in objTag:                       # 打印列表元素内容
    print(data.text)

执行结果

数据型态    =  <class 'bs4.element.ResultSet'>
打印Tag列表 =  [<h1 id="author">晓波</h1>, <h1 id="content">一个人的极境旅行 - 南极大陆北极海</h1>, <h1 id="content">HTML5+CSS3王者归来</h1>]
以下是打印列表元素 :
晓波
一个人的极境旅行 - 南极大陆北极海
HTML5+CSS3王者归来

21-4-7 认识HTML元素上下文属性与getText( )

HTML元素内容的属性有下列3种。

textContent:内容,不含任何标签码。

innerHTML:元素内容,含子卷标码,但是不含本身标签码。

outerHTML:元素内容,含子卷标码,也含本身标签码。

如果有一个元素内容如下:

 <p>Marching onto the path of <b>Web Design Expert</b></p>

则上述3个属性的观念与内容分别如下:
在这里插入图片描述

textContent:Web Design Expert

innerHTML:Marching onto the path of Web Design Expert

outerHTML:

Marching onto the path of Web Design Expert

当使用BeautifulSoup模块解析HTML文件时,如果传回的是列表,也可以配合索引应用getText( )取得列表元素内容,所取得的内容是textContent。意义与21-4-4小节的text属性相同。

程序实例ch21_17.py:使用getText( )重新扩充设计ch21_16.py。

# ch21_17.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.find_all('h1')
print("数据型态    = ", type(objTag))     # 打印数据型态
print("打印Tag列表 = ", objTag)           # 打印列表
print("\n使用Text属性打印列表元素 : ")
for data in objTag:                       # 打印列表元素内容
    print(data.text)
print("\n使用getText()方法打印列表元素 : ")
for i in range(len(objTag)):
    print(objTag[i].getText())

执行结果

数据型态    =  <class 'bs4.element.ResultSet'>
打印Tag列表 =  [<h1 id="author">晓波</h1>, <h1 id="content">一个人的极境旅行 - 南极大陆北极海</h1>, <h1 id="content">HTML5+CSS3王者归来</h1>]

使用Text属性打印列表元素 :
晓波
一个人的极境旅行 - 南极大陆北极海
HTML5+CSS3王者归来

使用getText()方法打印列表元素 :
晓波
一个人的极境旅行 - 南极大陆北极海
HTML5+CSS3王者归来

21-4-8 select( )

select( )主要是以CSS选择器(selector)的观念寻找元素,如果找到,回传的是列表(list),如果找不到则传回空列表。下列是使用实例:

objSoup.select(‘p’):找寻所有

卷标的元素。

objSoup.select (‘img’):找寻所有卷标的元素。

objSoup.select (‘.happy’):找寻所有CSS class属性为happy的元素。

objSoup.select (‘#author’):找寻所有CSS id属性为author的元素。

objSoup.select (‘p #author’):找寻所有

且id属性为author的元素。

objSoup.select (‘p .happy’):找寻所有

且class属性为happy的元素。

objSoup.select (‘div strong’):找寻所有在

元素内的 元素。

objSoup.select (‘div > strong’):找寻所有在

内的 元素,中间没有其他元素。

objSoup.select (‘input[name]’):找寻所有卷标且有name属性的元素。

程序实例ch21_18.py:找寻id属性是author的内容。

# ch21_18.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.select('#author')
print("数据型态     = ", type(objTag))          # 打印数据型态
print("列表长度     = ", len(objTag))           # 打印列表长度
print("元素数据型态 = ", type(objTag[0]))       # 打印元素数据型态
print("元素内容     = ", objTag[0].getText())   # 打印元素内容

执行结果

数据型态     =  <class 'bs4.element.ResultSet'>
列表长度     =  1
元素数据型态 =  <class 'bs4.element.Tag'>
元素内容     =  晓波

上述在使用时如果将元素内容当作参数传给str( ),将会传回含开始和结束卷标的字符串。

程序实例ch21_19.py:将解析的列表元素传给str( ),同时打印执行结果。

# ch21_19.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.select('#author')
print("列出列表元素的数据型态    = ", type(objTag[0]))
print(objTag[0])
print("列出str()转换过的数据型态 = ", type(str(objTag[0])))
print(str(objTag[0]))

执行结果

列出列表元素的数据型态    =  <class 'bs4.element.Tag'>
<h1 id="author">晓波</h1>
列出str()转换过的数据型态 =  <class 'str'>
<h1 id="author">晓波</h1>

尽管上述第8行与第10行打印的结果相同,但是第10行是纯字符串,第8行是卷标字符串,意义不同,未来可使用的方法也不同,将在21-4-9小节解说。

列表元素有attrs属性,如果使用此属性可以得到一个字典结果。

程序实例ch21_20.py:将attrs属性应用在列表元素,列出字典结果。

# ch21_20.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
objTag = objSoup.select('#author')
print(str(objTag[0].attrs))

执行结果

{'id': 'author'}

在HTML文件中常常可以看到卷标内有子卷标,如果查看myhtml.html的第21行,可以看到

卷标内有卷标,碰上这种状况若是打印列表元素内容时,可以看到子标签存在。但是,若是使用getText( )取得元素内容,可以得到没有子卷标的字符串内容。

程序实例ch21_21.py:搜寻

标签,最后列出列表内容与不含子卷标的元素内容。

# ch21_21.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
pObjTag = objSoup.select('p')
print("含<p>标签的列表长度 = ", len(pObjTag))
for i in range(len(pObjTag)):
    print(str(pObjTag[i]))              # 内部有子卷标<strong>字符串
    print(pObjTag[i].getText())         # 没有子标签
    print(pObjTag[i].text)              # 没有子标签

执行结果

<p>标签的列表长度 =  2
<p>2015/2016<strong>晓波</strong>一个人到南极</p>
2015/2016年晓波一个人到南极
2015/2016年晓波一个人到南极
<p>本书讲解网页设计使用HTML5+CSS3</p>
本书讲解网页设计使用HTML5+CSS3
本书讲解网页设计使用HTML5+CSS3

21-4-9 卷标字符串的get( )

假设我们现在搜寻标签,请参考下列实例。

程序实例ch21_22.py:搜寻标签,同时列出结果。

# ch21_22.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
imgTag = objSoup.select('img')
print("含<img>标签的列表长度 = ", len(imgTag))
for i in range(len(imgTag)):              
    print(imgTag[i])     

执行结果

<img>标签的列表长度 =  3
<img src="hung.jpg" width="100"/>
<img src="travel.jpg" width="300"/>
<img src="html5.jpg" width="300"/>

是一个插入图片的卷标,没有结束卷标,所以没有内文,如果读者尝试使用text属性打印内容“print(imgTag[0].text)”将看不到任何结果。对网络爬虫设计很重要,因为可以由此获得网页的图文件信息。从上述执行结果可以看到,对我们而言很重要的是卷标内的属性src,这个属性设定了图片路径。这个时候我们可以使用卷标字符串的get( )取得。

程序实例ch21_23.py:扩充ch21_22.py,取得myhtml.html的所有图片文件。

# ch21_23.py
import bs4

htmlFile = open('myhtml.html', encoding='utf-8')
objSoup = bs4.BeautifulSoup(htmlFile, 'lxml')
imgTag = objSoup.select('img')
print("含<img>标签的列表长度 = ", len(imgTag))
for i in range(len(imgTag)):              
    print("打印标签列表 = ", imgTag[i])
    print("打印图档     = ", imgTag[i].get('src'))

执行结果

<img>标签的列表长度 =  3
打印标签列表 =  <img src="hung.jpg" width="100"/>
打印图档     =  hung.jpg
打印标签列表 =  <img src="travel.jpg" width="300"/>
打印图档     =  travel.jpg
打印标签列表 =  <img src="html5.jpg" width="300"/>
打印图档     =  html5.jpg

上述程序最重要是第10行的imgTag[i].get(‘src’),这个方法可以取得卷标字符串的src属性内容。在程序实例ch21_19.py,笔者曾经说明卷标字符串与纯字符串(str)不同就是在这里,纯字符串无法调用get( )方法执行上述将图文件字符串取出的动作。

21-5 网络爬虫实战

其实笔者已经用HTML文件解说网络爬虫的基本原理了,而在真实的网络世界一切比上述实例复杂困难。

程序实例ch21_24.py:这个程序将至上奇信息网页下载所有图片,所下载的图片将放到目前文件夹的out21_24内,信息网址如下:

https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9496705306045101867%22%7D&n_type=-1&p_from=-1

# ch21_24.py
import bs4, requests, os

url = 'https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9496705306045101867%22%7D&n_type=-1&p_from=-1'                  # 信息网页
html = requests.get(url)
print("网页下载中 ...")
html.raise_for_status()                             # 验证网页是否下载成功                      
print("网页下载完成")

destDir = 'out21_25'                                # 设定未来储存图片的文件夹
if os.path.exists(destDir) == False:
    os.mkdir(destDir)                               # 建立文件夹供未来储存图片

objSoup = bs4.BeautifulSoup(html.text, 'lxml')      # 建立BeautifulSoup对象

imgTag = objSoup.select('img')                      # 搜寻所有图片档案
print("搜寻到的图片数量 = ", len(imgTag))           # 列出搜寻到的图片数量
if len(imgTag) > 0:                                 # 如果有找到图片则执行下载与储存
    for i in range(len(imgTag)):                    # 循环下载图片与储存
        imgUrl = imgTag[i].get('src')               # 取得图片的路径
        print("%s 图片下载中 ... " % imgUrl)
        finUrl = url + imgUrl                       # 取得图片在Internet上的路径
        print("%s 图片下载中 ... " % finUrl)
        picture = requests.get(finUrl)              # 下载图片
        picture.raise_for_status()                  # 验证图片是否下载成功
        print("%s 图片下载成功" % finUrl)

        # 先开启档案, 再储存图片
        pictFile = open(os.path.join(destDir, os.path.basename(imgUrl)), 'wb')
        for diskStorage in picture.iter_content(10240):
            pictFile.write(diskStorage)
        pictFile.close()                            # 关闭档案

执行结果

网页下载中 ...
网页下载完成
搜寻到的图片数量 =  21
https://mbdp01.bdstatic.com/static/landing-pc/img/logo_top.79fdb8c2.png 图片下载中 ... 
https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9496705306045101867%22%7D&n_type=-1&p_from=-1https://mbdp01.bdstatic.com/static/landing-pc/img/logo_top.79fdb8c2.png 图片下载中 ...
https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9496705306045101867%22%7D&n_type=-1&p_from=-1https://mbdp01.bdstatic.com/static/landing-pc/img/logo_top.79fdb8c2.png 图片下载成功

上述程序大部分皆已做过解说,最重要是第29行,因为所搜寻到的图片imgURL可能是位于其他子文件夹,它的文件名前方有目录路径,os.path.basename( )主要是忽略目录路径传回程序文件名,这样就可以避免因为需要在desrDir下打开不存在的文件夹而产生错误。例如、imgUrl是\image\sample.jpg,如果没有os.path.basename( ),传回结果是:

 out21_24\image\sample.jpg  # 因为image文件夹不存在打开时会有错误

有了os.path.basename( ),传回结果是:

 out21_24\sample.jpg  # 可以正常打开此文件

在21-2-6小节笔者有介绍有些网站的服务器会挡住网络爬虫的需求,所以必须在Python程序前方加上伪装成服务器的header定义,当时有用程序实例ch21_9_2.py设计一个程序,下列是这个程序的实际应用。

程序实例ch21_25.py:笔者将myhtml.html文件放上网络,改名index.html,这个程序会下载这个网页的图片,现在可以使用下列网址浏览此网页:

http://aaa.24ht.com.tw/

# ch21_25.py
import bs4, requests, os

headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64)\
            AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101\
            Safari/537.36', }
url = 'http://aaa.24ht.com.tw/'                     # 这个服务器会挡住网页
html = requests.get(url, headers=headers)           
print("网页下载中 ...")
html.raise_for_status()                             # 验证网页是否下载成功                      
print("网页下载完成")

destDir = 'out21_25'                                # 设定储存文件夹
if os.path.exists(destDir) == False:
    os.mkdir(destDir)                               # 建立目录供未来储存图片

objSoup = bs4.BeautifulSoup(html.text, 'lxml')      # 建立BeautifulSoup对象

imgTag = objSoup.select('img')                      # 搜寻所有图片档案
print("搜寻到的图片数量 = ", len(imgTag))           # 列出搜寻到的图片数量
if len(imgTag) > 0:                                 # 如果有找到图片则执行下载与储存
    for i in range(len(imgTag)):                    # 循环下载图片与储存
        imgUrl = imgTag[i].get('src')               # 取得图片的路径
        print("%s 图片下载中 ... " % imgUrl)
        finUrl = url + imgUrl                       # 取得图片在Internet上的路径
        print("%s 图片下载中 ... " % finUrl)
        picture = requests.get(finUrl, headers=headers) # 下载图片
        picture.raise_for_status()                  # 验证图片是否下载成功
        print("%s 图片下载成功" % finUrl)

        # 先开启档案, 再储存图片
        pictFile = open(os.path.join(destDir, os.path.basename(imgUrl)), 'wb')
        for diskStorage in picture.iter_content(10240):
            pictFile.write(diskStorage)
        pictFile.close()                            # 关闭档案

执行结果 所下载的图片会放在out21_25文件夹。

网页下载中 ...
网页下载完成
搜寻到的图片数量 =  5
hung.jpg 图片下载中 ...
http://aaa.24ht.com.tw/hung.jpg 图片下载中 ...
http://aaa.24ht.com.tw/hung.jpg 图片下载成功
travel.jpg 图片下载中 ...
http://aaa.24ht.com.tw/travel.jpg 图片下载中 ...
http://aaa.24ht.com.tw/travel.jpg 图片下载成功
html5.jpg 图片下载中 ...
http://aaa.24ht.com.tw/html5.jpg 图片下载中 ...
http://aaa.24ht.com.tw/html5.jpg 图片下载成功
/bitnami/images/close.png 图片下载中 ...
http://aaa.24ht.com.tw//bitnami/images/close.png 图片下载中 ...
http://aaa.24ht.com.tw//bitnami/images/close.png 图片下载成功
/bitnami/images/corner-logo.png 图片下载中 ...
http://aaa.24ht.com.tw//bitnami/images/corner-logo.png 图片下载中 ...
http://aaa.24ht.com.tw//bitnami/images/corner-logo.png 图片下载成功

程序实例ch21_26.py:找出台湾彩劵公司106000085期威力彩开奖结果。这个程序在设计时,第12行我们列出先找寻Class是“contents_box02”,因为我们发现这里有记载106000085期的开奖结果。这个程序会随时间不同而有不同的日期期数开奖结果。

在这里插入图片描述

结果程序第13行发现有4组Class是“contents_box02”,程序第14和15行则列出这4组列表。

# ch21_26.py
import bs4, requests

url = 'http://www.taiwanlottery.com.tw'
html = requests.get(url)
print("网页下载中 ...")
html.raise_for_status()                             # 验证网页是否下载成功                      
print("网页下载完成")

objSoup = bs4.BeautifulSoup(html.text, 'lxml')      # 建立BeautifulSoup对象

dataTag = objSoup.select('.contents_box02')         # 寻找class是contents_box02
print("列表长度", len(dataTag))
for i in range(len(dataTag)):                       # 列出含contents_box02的列表                 
    print(dataTag[i])
        
# 找寻开出顺序与大小顺序的球
balls = dataTag[0].find_all('div', {'class':'ball_tx ball_green'})
print("开出顺序 : ", end='')
for i in range(6):                                  # 前6球是开出顺序
    print(balls[i].text, end='   ')

print("\n大小顺序 : ", end='')
for i in range(6,len(balls)):                       # 第7球以后是大小顺序
    print(balls[i].text, end='   ')

# 找出第二区的红球                   
redball = dataTag[0].find_all('div', {'class':'ball_red'})
print("\n第二区   :", redball[0].text)

执行结果

在这里插入图片描述

由于我们发现106000085期威力彩是在第一个列表,所以程序第18行,使用下列指令。

 balls = dataTag[0].find_all(‘div', {‘class':‘ball_tx ball_green'})

dataTag[0]代表找寻第1组列表元素,find_all( )是找寻所有标签是‘div’,此标签类别class是“ball_tx ball_green”的结果。经过这个搜寻可以得到balls列表,然后第20和21行列出开球顺序。程序第24和25行列出号码球的大小顺序。

程序第28行也可以改用find( ),因为只有一个红球是特别号。这是找寻所有卷标是‘div’,此标签类别class是“ball_red”的结果。

21-6 命令行窗口

其实一般的计算机用户是不会用到命令行窗口,这是最早期DOS(Disk Operating System)操作系统时的环境,现在大多数应用程序在安装时,已经将应用程序打包成一个图标,只要点选图标即可操作。但是,如果想要成为计算机高手,常会需要额外安装一些应用软件,这些应用软件需要在命令行窗口安装或设定,本节笔者将讲解Python程序在命令行窗口执行的方法,以及说明程序执行的参数。

Windows 7是在开始菜单内,输入CMD,回车。
在这里插入图片描述

执行后可以看到下列命令行窗口。
在这里插入图片描述

我们除了可以在Python的IDLE窗口执行程序,也可以在命令行环境执行Python程序,假设要执行的程序是d:\Python\ch21\ch21_27.py(这是笔者目前程序所在位置),方法如下:

 python安装路径\python d:\Python\ch21\ch21_27.py

如果程序执行时有参数,则参数在空一格后,放在右边,如下所示:

python安装路径\python d:\Python\ch21\ch21_27.py 参数1 … 参数n

其实在Python程序设计中d:\Python\ch21\ch21_27.py会被当作命令提示列表的第0个元素,如果有其他参数存在,则会依次当作第1个元素,……接着笔者将测试这个观念,首先要导入sys模块,如下所示:

 import sys

当我们导入上述模块后,命令提示列表的名称是sys.argv。

程序实例ch21_27.py:打印命令提示列表的第0个元素,这个程序需在命令行窗口执行。

# ch21_27.py
import sys

print(sys.argv[0])

执行结果
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值