Python爬虫入门——信息组织与提取方法(2)

1. 信息提取的一般方法

  指从标记的信息中提取关注的内容。上一章提到的信息标记有三种形式:XML、JSON、YAML。
一般意义上的几种方法:
方法一:完整的解析信息的标记形式,再提取关键信息。像XML、JSON、YAML等,需要标记解析器,例如bs4库的标签树遍历,需要解析什么信息,去遍历这棵树就ok了。
优点:信息解析准确, 缺点:提取过程繁琐,速度慢。

方法二:无视任何标记信息,直接搜索关键信息。就像在一个Word文档中搜索关键词一样,根本不需要去关心文档具有什么样的标题形式和格式,只需要我们对信息的文本查找函数即可。
优点:提取过程简单,速度较快。 缺点:提取结果缺乏准确性。

二者方法哪种好呢?现实生活中我们使用的,是一种融合的方法。
融合方法:结合形式解析与搜索方法,来提取关键信息。

2. 一个小例子

例子的原网页:http://python123.io/ws/demo.html
实例:提取HTML中所有URL链接。
思路:(1)观察网页源代码,发现所有的URL链接都在< a>标签中。
   (2)搜索到所有的< a>标签
   (3)解析< a>标签格式,提取herf后的链接内容。

import requests
from bs4 import BeautifulSoup      #BeautifulSoup是一个类
r=requests.get("http://python123.io/ws/demo.html")
r.encoding=r.apparent_encoding
demo=r.text
soup=BeautifulSoup(demo,"html.parser")    #两个参数,第一个是要解析的文章,第二个是“html的解析器”
for link in soup.find_all('a'):
	print(link.get('href'))      #这里的find_all方法,待会会在后面讲。

3. 基于bs4库的HTML内容查找方法

上面讲到的<>.find_all(name,attrs,rescursive,string,**kwargs)
返回一个列表类型,存储查找的结果。各参数的说明如下:

  • name:对标签名称的检索字符串
>>> soup.find_all('a')
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]
>>> soup.find_all(['a','b'])
[<b>The demo python introduces several python courses.</b>, <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]
>>> for tag in soup.find_all(True):  #当参数为True时,默认是所有的标签
		print(tag.name)

	
html
head
title
body
p
b
p
a
a
>>> for tag in soup.find_all(re.compile('b')): #打印以b开头的标签
		print(tag.name)

	
body
b
  • attrs:对标签属性值的检索字符串,可以标注属性检索
>>> soup.find_all('p','course')   #对p标签的course属性值进行搜索
[<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:
<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> and <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>.</p>]
>>> soup.find_all(id='link1')   #对所有标签的id=link1属性,进行搜索
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>]
>>> soup.find_all(id='link')
[]
>>> soup.find_all(id=re.compile('link'))    #所有以link开头的id
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]
  • recursive:是否对子孙全部检索,默认为True
>>> soup.find_all('a')
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]
>>> soup.find_all('a',recursive=False)
[]
  • string:<>…< />中字符串区域的检索字符串
>>> soup.find_all(string='Basic Python')
['Basic Python']
>>> soup.find_all(string=re.compile('python'))   #搜索:字符串区域出现过‘python’字样的标签
['This is a python demo page', 'The demo python introduces several python courses.']

find_all()函数非常常用,所有有一种简写形式

< tag>(…)等价于< tag>.find_all(…)
soup(…)等价于soup.find_all(…)

扩展方法:

方法 说明
<>.find() 搜索且返回一个结果,字符串类型。同.find_all()参数。
<>.find_parents() 在先辈节点中搜索,返回列表类型。同.find_all()参数。
<>.find_parent() 在先辈节点中返回一个结果,字符串类型。同.find_all()参数。
<>.find_next_sibling() 在后续平行节点中返一个结果,字符串类型。同.find_all()参数。
<>.find_next_siblings() 在后续平行节点中搜索,返回列表类型。同.find_all()参数。
<>.find_previous_siblings() 在前续平行节点中搜索,返回列表类型。同.find_all()参数。
<>.find_previous_sibling() 在前续平行节点中返一个结果,字符串类型。同.find_all()参数。

4. 实战:中国大学排名爬虫

技术路线:requests—bs4
定向爬虫,仅对输入URL进行爬取,不扩展爬取。

 4.1 分析

爬取网页的链接:http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html
先打开网页,查看源代码。

步骤一:确定可行性,确定我们想要的内容在html代码中可以找到。因为有一部分数据可能是通过JavaScripe等脚本语言生成。当你访问网页的时候,他的信息是动态生成的。在这种情况下,request库和bs4是无法提取的。爬取动态网页的方法以后会讲,这里先爬取静态网页。

步骤二:查看robots协议,发现它对爬虫没有限制。

步骤三:功能描述:打印出排名、大学名称、和总分。我们要在HTML文档中找到我们的目标的具体位置。

步骤四:对程序结构进行设计。

  • 从网络上获取大学排名网页内容——getHTMLText()函数实现
  • 提取网页内容中信息到合适的数据结构内(重点)——fillUnivList()函数实现
  • 利用数据结构展示并输出结果——printUnivList()函数实现

 4.2 实战

(1) 从网络上获取大学排名网页内容——getHTMLText()函数实现,这段代码比较简单,就不再具体分析了。

def getHTMLText(url):
    try:
        r=requests.get(url,timeout=30)
        r.raise_for_status()
        r.encoding=r.apparent_encoding
        return r.text
    except:
        return ""

(2)这段代码,是提取网页内容中需要的信息,到合适的数据结构内——fillUnivList()函数实现

  • 先要看懂HTML代码,知道我们的目标 “排名、大学名称、和总分”在哪个标签下面。然后找到这个标签。
  • 看过之后,发现,我们需要的内容都在一个叫<tbody>的标签下面。
  • 再对tbody这个标签具体分析,使用soup.tbody.prettify()打印出这个标签的标签树结构。分析得知,每个大学的信息都在tbody标签下面的tr标签里面。将tr标签打印出来如下(soup.tbody.tr.prettify())
<tr class="alt">
 <td>
  1
 </td>
 <td>
  <div align="left">
   清华大学
  </div>
 </td>
 <td>
  北京市
 </td>
 <td>
  95.9
 </td>
 <td class="hidden-xs need-hidden indicator5">
  100.0
 </td>
 <td class="hidden-xs need-hidden indicator6" style="display:none;">
  97.90%
 </td>
 <td class="hidden-xs need-hidden indicator7" style="display:none;">
  37342
 </td>
 <td class="hidden-xs need-hidden indicator8" style="display:none;">
  1.298
 </td>
 <td class="hidden-xs need-hidden indicator9" style="display:none;">
  1177
 </td>
 <td class="hidden-xs need-hidden indicator10" style="display:none;">
  109
 </td>
 <td class="hidden-xs need-hidden indicator11" style="display:none;">
  1137711
 </td>
 <td class="hidden-xs need-hidden indicator12" style="display:none;">
  1187
 </td>
 <td class="hidden-xs need-hidden indicator13" style="display:none;">
  593522
 </td>
</tr>

  • 这样就一目了然了,大学的排名是第一个td标签中的字符串,大学名字在第二个td标签中的字符串,总分在第三个td标签中的字符串。下面就是打印了。具体代码如下(一定要好好看注释!!!):
def fillUnivList(ulist,html):
   soup=BeautifulSoup(html,"html.parser") 
   for tr in soup.find('tbody').children:    #tr是tbody的儿子节点。   .children返回的是一个迭代类型
       if isinstance(tr,bs4.element.Tag):    #isinstance() 函数来判断一个对象是否是一个已知的类型,类似type()。 判断tr是否是一个标签类型
           tds=tr("td")   #等同于<tr>.find_all("td"),返回一个列表类型,列表中是一个tr标签下的所有td标签
           ulist.append([tds[0].string,tds[1].string,tds[2].string])

(3)利用数据结构展示并输出结果——printUnivList()函数实现

def printUnivList(ulist,num):           #\t相当于tab键
    tplt="{0:^10}\t{1:{3}^10}\t{2:^10}"  #{3}表示,当打印学校名字时,采用format函数第三个变量来填充,也就是使用中文空格来填充。
    print(tplt.format("排名","学校","分数",chr(12288)))    #中文对齐问题,采用中文字符填充
    for i in range(num):
        u=ulist[i]
        print(tplt.format(u[0],u[1],u[2],chr(12288)))

这里需要用到Python中的format()函数,就不再讲解,具体的,可以自行百度。

选项 含义
‘<’ 强制字段在可用空间内左对齐(这是大多数对象的默认设置)。
‘>’ 强制字段在可用空间内右对齐(这是数字的默认值)。
‘^’ 强制字段在可用空间内居中。

 4.3 完整代码

import requests
import bs4
from bs4 import BeautifulSoup      #BeautifulSoup是一个类

def getHTMLText(url):
    try:
        r=requests.get(url,timeout=30)
        r.raise_for_status()
        r.encoding=r.apparent_encoding
        return r.text
    except:
        return ""

def fillUnivList(ulist,html):
    soup=BeautifulSoup(html,"html.parser") 
    for tr in soup.find('tbody').children:    #tr是tbody的儿子节点,这是一个迭代类型
        if isinstance(tr,bs4.element.Tag):    #isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。 
            tds=tr("td")   #等同于tr.find_all(“td”),返回一个列表类型
            ulist.append([tds[0].string,tds[1].string,tds[2].string])

def printUnivList(ulist,num):           #\t相当于tab键
    tplt="{0:^10}\t{1:{3}^10}\t{2:^10}"
    print(tplt.format("排名","学校","分数",chr(12288)))    #中文对齐问题
    for i in range(num):
        u=ulist[i]
        print(tplt.format(u[0],u[1],u[2],chr(12288)))

def main():
    uinfo=[]
    url="http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html"
    html=getHTMLText(url)
    fillUnivList(uinfo,html)
    printUnivList(uinfo,20)   #只打印前20所学校

main()


5. 总结

  文章讲解了信息提取的一般方法。利用BeautifulSoup库和Requests库,一般来说信息提取的步骤大概可总结为:找到信息的具体位置(搜索),然后再用解析器进行局部遍历(形式解析),找到目标打印。
  文章有些例子用到了Re正则表达式(但很少),正则表达式还是很重要的,在后面会接着详解。请多关注~!

发布了20 篇原创文章 · 获赞 51 · 访问量 7513

猜你喜欢

转载自blog.csdn.net/weixin_43275558/article/details/104456987