CH2 網(wǎng)絡(luò)爬蟲提取

網(wǎng)絡(luò)爬蟲提取

[TOC]

1. Beautiful Soup庫入門

bs庫的安裝

win平臺下横媚,cmd輸入

pip install beautifulsoup4

beautifulsoup的基本結(jié)構(gòu)

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')

BeautifulSoup庫的基本元素

對于bs庫的理解

? BeautifulSoup庫是解析先改、遍歷、維護(hù)”標(biāo)簽樹“的功能庫翎冲,什么是標(biāo)簽樹呢,HTML語言是標(biāo)記語言捐祠,通過不同的標(biāo)簽將內(nèi)容分割為不同的結(jié)構(gòu)眶熬,因此HTML的文本可以通過標(biāo)簽生成一顆標(biāo)簽樹,bs庫通過對這個樹進(jìn)行不同的操作勃黍。

? 一個標(biāo)簽宵统,例如<p>..</p>``<a>..</a>等是以一個標(biāo)簽:Tag的形式出現(xiàn),p a是tag的名稱Name覆获,而后面跟的鍵值對是它的屬性Attributes马澈,一個HTML文檔 = 一顆標(biāo)簽樹 = 一個BeautifulSoup類實例 ,它對應(yīng)的是HTML/XML文檔的全部內(nèi)容

bs庫解析器

soup = BeautifulSoup('<html>data</html>','html.parser')

bs支持多種不同的解析器分別為

解析器 使用方法 條件
bs4的HTML解析器 BeautifulSoup('<html>data</html>','html.parser') 安裝bs4庫
lxml的HTML解析器 BeautifulSoup('<html>data</html>','lxml') pip install lxml
lxml的XML解析器 BeautifulSoup('<html>data</html>','xml') pip install lxml
html5lib的解析器 BeautifulSoup('<html>data</html>','html5lib') pip install html5lib

BeautifulSoup類的基本元素

基本元素 說明
Tag 標(biāo)簽弄息,最基本信息組織單元<></>
Name 標(biāo)簽名字,比如p痊班,a,格式<tag>.name
Attributes 標(biāo)簽屬性摹量,字典形式格式<tag>.attrs
NavigableString 標(biāo)簽內(nèi)非屬性的字符串<>和</>中間的字符串涤伐,格式:<tag>.string
Comment 標(biāo)簽內(nèi)字符串的注釋部分,特殊Comment類型

下面來說明一下這些都是怎么用的

  • Tag

    ? 因為不同的標(biāo)簽都有不同的名字所以任何存在與HTML語法中的標(biāo)簽都可以用soup.<tag>訪問獲得缨称,如果存在多個<tag>對應(yīng)內(nèi)容時废亭,返回第一個

    ? 比如,soup為經(jīng)過處理的bs類

    soup = BeautifulSoup(demo, 'html.parser')
    

    如果需要訪問title標(biāo)簽具钥,輸入

    soup.title
    

    如果需要訪問a標(biāo)簽

    soup.a
    tag = soup.a
    

    這樣就取得了一個Tag豆村,后續(xù)可以對這個Tag做其他操作

  • Name

    每個Tag都有自己的名字,通過<tag>.name獲取骂删,字符串類型

    >>> soup.a.name
    'a'
    >>> soup.a.parent.name    #雙親的名字
    'p'
    >>> soup.a.parent.parent.name
    'body'
    
  • Attributes

    ? 一個<tag>可以有多個屬性或者0個掌动,字典類型四啰,使用<tag>.attrs可以以字典形式顯示所有屬性,由于是字典類型粗恢,可以通過Dict['name']取到對應(yīng)的值

    >>> tag = soup.a
    >>> tag.attrs
    {'id':'link1','class':['py1'],'href':'xxx'}
    >>> tag.attrs['class']
    ['py1']
    >>> tag.attrs['href']
    'xxx'    #注意是字符串形式
    
  • NavigableString

    ? 代表標(biāo)簽內(nèi)非屬性字符串柑晒,注意,NavigableString可以跨越多個層次眷射,即匙赞,如果string外面有兩個標(biāo)簽,取最外面的標(biāo)簽<tag>.string取到的是兩個標(biāo)簽夾著的中間字符串妖碉。

  • Comment

    Comment是一種特殊類型的String涌庭,代表注釋

    >>> type(soup.b.string)    #b中間是Comment,<!--...-->
    <class 'bs4.element.Comment'>
    

Beautiful Soup庫的3種遍歷方式

? 對于標(biāo)簽樹欧宜,一共有3種遍歷方式坐榆,分別是:上行遍歷,下行遍歷冗茸,平行遍歷

下行遍歷

下行遍歷有三種屬性

屬性 說明
.contents 子結(jié)點的列表席镀,將<tag>所有兒子節(jié)點存入列表
.children 子結(jié)點的迭代類型,與.contents類似夏漱,用于循環(huán)遍歷兒子結(jié)點
.descendants 子孫結(jié)點的迭代類型豪诲,包含所有子孫結(jié)點,用于循環(huán)遍歷

BeautifulSoup類是根節(jié)點挂绰,<head></head>標(biāo)簽單獨作為一例屎篱,中間包含title標(biāo)簽,<body></body>中間包含<p><a>標(biāo)簽等扮授。

注:在.contents屬性中,'\n'也是作為單獨的一個兒子節(jié)點被存入

下行遍歷的結(jié)構(gòu)
  1. 遍歷兒子節(jié)點

    for child in soup.body.children:
        print(child)
    
  1. 遍歷子孫節(jié)點

    for child in soup.body.descendants:
        print(child)
    

上行遍歷

屬性 說明
.parent 結(jié)點的父親標(biāo)簽
.parents 結(jié)點先輩標(biāo)簽的迭代類型专肪,用于循環(huán)遍歷先輩結(jié)點

注:soup類本身的父結(jié)點為空刹勃,所以如果進(jìn)行迭代,要進(jìn)行判斷

上行遍歷的結(jié)構(gòu)
for parent in soup.a.parents:
    if parent is None:    #判斷是否為最頂結(jié)點
        print(parent)
    else:
        print(parent.name)

平行遍歷

屬性 說明
.next_sibling 返回按照HTML文本順序的下一個平行結(jié)點標(biāo)簽
.previous_sibling 返回按照HTML文本順序的上一個平行結(jié)點標(biāo)簽
.next_siblings 迭代類型(迭代器)嚎尤,返回按照HTML文本順序的后續(xù)平行結(jié)點
.previous_siblings 迭代類型(迭代器)荔仁,返回按照HTML文本順序的前驅(qū)平行結(jié)點

注:平行遍歷是發(fā)生在同一父結(jié)點下的各結(jié)點

平行遍歷的結(jié)構(gòu)
  1. 遍歷后續(xù)結(jié)點

    for sibling in soup.a.next_siblings:
        print(sibling)
    
  1. 遍歷前驅(qū)結(jié)點

    for sibling in soup.a.previous_siblings:
        print(sibling)
    

bs4的prettify方法

? bs4庫的prettify()方法為HTML文本<>及其內(nèi)容增加換行符

prettify方法可用于標(biāo)簽或者BeautifulSoup類型,格式為

<tag>.prettify()    

2. 信息組織與提取方法

信息標(biāo)記的三種形式

XML JSON YAML

XML

XML全稱為eXtensible Markup Language芽死,是一種標(biāo)記語言

<img src="china.jpg" size="10">...</img>

帶名稱和屬性的標(biāo)簽乏梁,中間還有非標(biāo)簽元素

<img src="china.jpg" size="10" />

空元素書寫形式

<!-- This is a comment-->

注釋

JSON

JavaScript Object Notation 有類型的鍵值對 key:value

比如:

"name":"哈爾濱工業(yè)大學(xué)"

而一個關(guān)鍵字對應(yīng)多個值時

"name":["哈爾濱工業(yè)大學(xué)","西大直街"]

如果出現(xiàn)鍵值對的嵌套

"name":{
    "newName":"圣馬家溝職業(yè)學(xué)院",
    "oldName":"哈爾濱工業(yè)大學(xué)"
}

YAML

YAML Ain`t Markup Language 無類型鍵值對, key:value僅用字符串就可以表達(dá)關(guān)系

name:哈爾濱工業(yè)大學(xué)

利用縮進(jìn)表達(dá)所屬關(guān)系

name:
    newName:圣馬家溝職業(yè)學(xué)院
    oldName:哈爾濱工業(yè)大學(xué)

-表達(dá)并列關(guān)系

name:
-哈爾濱工業(yè)大學(xué)
-西大直街

| 表達(dá)整塊數(shù)據(jù)关贵,如果數(shù)據(jù)很多的話遇骑,# 表示注釋

text: |  #introduction
balabalabala...

舉個例子區(qū)分:

描述一個學(xué)生小明

XML:

<person>
    <firstname>Ming</firstname>
    <lastname>Xiao</lastname>
    <address>
        <street>西大直街</street>
        <city>哈爾濱</city>
    </address>
    <prof>CS</prof><prof>SE</prof>
</person>

JSON

"person":{
    "firstName":"Ming",
    "lastName":"Xiao",
    "address":{
                "street":"西大直街"
                "city":"哈爾濱"
              },
    "prof":["CS","SE"]
}

YAML

person:
    firstname:Ming
    lastName:Xiao
    address:
        street:西大直街
        city:哈爾濱
    prof:
    -CS
    -SE

三種信息標(biāo)記比較:

XML 最早的標(biāo)記語言,拓展性好揖曾,繁瑣

JSON 信息有類型落萎,適合程序處理亥啦,較XML簡潔(什么叫有類型,JSON中每個關(guān)鍵字有""標(biāo)記练链,”“叫做類型翔脱,”xxx“叫做鍵key

YAML 信息無類型,單純?yōu)樽址焦模勺x性好

信息提取的一般方法

方法一:完整解析信息的標(biāo)記形式届吁,在提取關(guān)鍵信息

? 需要標(biāo)記解析器,例如bs4庫的標(biāo)簽樹绿鸣,信息提取準(zhǔn)確但是提取過程很繁瑣疚沐,速度慢

方法二:無視標(biāo)記形式,直接搜索關(guān)鍵信息

? 需要文本查找函數(shù)枚驻,提取過程簡潔速度快濒旦,但是提取的結(jié)果準(zhǔn)確性與信息內(nèi)容有關(guān)

基于bs4庫的HTML查找方法

<>.find_all方法

<>.find_all方法的實現(xiàn)為

<>.find_all(name, attrs, recursive, string, **kw)

函數(shù)返回的是一個列表LIST類型,存儲查找的結(jié)果再登,注意:此時列表中的是Tag類型實例尔邓,其包含name、string等元素

  • name:對標(biāo)簽名稱的檢索字符串

對一個標(biāo)簽搜索

>>> soup.find_all('a')

對多個標(biāo)簽搜索

>>> soup.find_all(['a','b'])

也可以輸入?yún)?shù)True和正則表達(dá)式锉矢,True返回的結(jié)果是所有的標(biāo)簽

>>> for tag in soup.find_all(True):
        print(tag.name)

>>> import re
>>> for tag in soup.find_all(re.complie('b')):
        print(tag.name)

body
b

  • attrs:對標(biāo)簽屬性值的檢索字符串梯嗽,可標(biāo)注屬性檢索

    參數(shù)輸入可以是單個的鍵值對中的值,也可以是鍵值對沽损,也可以是正則表達(dá)式

    >>> soup.find_all('p','course')    #鍵值對中的值
    >>> soup.find_all(id='link1')    #鍵值對形式灯节,注意此時name可以省略,如果attrs參數(shù)不是鍵值對绵估,要加上name炎疆,否則函數(shù)會將attrs識別為name
    >>> soup.find_all(id=re.complie('link'))
    
    
  • recursive:是否對子孫全部檢索,默認(rèn)為True

    通過recursive=False``recursive=True設(shè)置

  • string:對標(biāo)簽中字符串區(qū)域檢索国裳,檢索結(jié)果為字符串

    支持正則表達(dá)式

    >>> soup.find_all(string='Python')
    
    

<>.find_all()的等價寫法

<tag>(...) <--> <tag>.find_all(...)

soup(...) <--> soup.find_all(...)

主要是因為這個方法使用頻率很高

<>.find_all的擴(kuò)展方法

方法 說明
<>.find() 搜索值返回一個結(jié)果
<>.find_parents() 在先輩節(jié)點中搜索形入,返回列表類型
<>.find_parent() 在先輩節(jié)點中返回一個結(jié)果
<>.find_next_siblings() 在后續(xù)平行節(jié)點中搜索,返回列表類型
<>.find_next_sibling() 在后續(xù)平行節(jié)點中返回一個結(jié)果
<>.find_previous_siblings() 在前序平行節(jié)點中搜索缝左,返回列表
<>.find_previous_sibling() 在前序平行節(jié)點中返回一個結(jié)果

至于為什么沒有對于子孫節(jié)點的搜索亿遂,是因為find_all() 和 find()函數(shù)就是這樣的功能

3. 正則表達(dá)式庫入門(re)

? RE,regular expression渺杉, regex蛇数,正則表達(dá)式是用來簡潔表達(dá)一組字符串的表達(dá)式,是一種通用的字符串表達(dá)框架是越。

? 在re庫中耳舅,首先使用正則表達(dá)式規(guī)則寫出字符串,然后通過re庫的編譯是字符串轉(zhuǎn)換為正則表達(dá)式特征倚评,再將特征與字符串匹配挽放。

正則表達(dá)式regex(string) => p = re.compile(regex)(編譯) => 特征p

正則表達(dá)式的語法

正則表達(dá)式常用操作符

操作符 說明 實例
. 表示任何單個字符
[ ] 字符集绍赛,對單個字符給出取值范圍 [abc]表示abc,[a-z]表示a到z單個字符
[^ ] 非字符集辑畦,對單個字符給出排除范圍 [^abc]表示非a或b或c的單個字符
* 前一個字符0次或無限次擴(kuò)展 abc*表示ab吗蚌、abc、abcccc
+ 前一個字符1次或無限次擴(kuò)展 abc+表示abc纯出、abcc蚯妇、abccc
前一個字符0次或1次擴(kuò)展 abc暂筝?表示ab箩言、abc
| 左右表達(dá)式任意一個 abc|edf表示abc、def
{m} 擴(kuò)展前一個字符m次 ab{2}表示abb
{m,n} 擴(kuò)展前一個字符m至n次(含n) ab{1,2}表示abc焕襟、abbc
^ 匹配字符串的開頭 ^abc表示abc且在一個字符串的開頭
$ 匹配字符串的結(jié)尾 abc$表示abc且在一個字符串的結(jié)尾
( ) 分組標(biāo)記陨收,內(nèi)部只能使用|操作符 (abc)表示abc,(abc|def)表示abc鸵赖、def 沒有或
\d 數(shù)字务漩,等價0-9
\w 單詞字符,等價[A-Za-z0-9_]

正則表達(dá)式的經(jīng)典實例

由26個字母組成的字符串 ^[A-Za-z]+$

由26個字母和數(shù)字組成的字符串 ^[A-Za-z0-9]+$

整數(shù)形式的字符串 ^-?\d+$

正整數(shù)形式的字符串 ^[0-9]*****[1-9][0-9]*****$

郵政編碼 [1-9]\d{5}

匹配中文字符 [\u4e00-\u9fa5]

匹配IP地址的正則表達(dá)式

0-99 :[1-9]?\d

100-199 :1\d{2}

200-249 :2[0-4]\d

250-255 :25[0-5]

表示為:(0-99|100-199|200-249|250-255.){3}(0-99|100-199|200-249|250-255)

Re庫介紹

? 正則表達(dá)式使用raw string類型(原生字符串類型)它褪,表示為r'text'饵骨。當(dāng)然也可以使用string類型表達(dá),但是\需要表示為\\

Re庫的主要功能函數(shù)

函數(shù) 說明
re.search() 在一個字符串中搜索匹配正則表達(dá)式的第一個位置茫打,返回match對象
re.match() 從一個字符串的開始位置匹配正則表達(dá)式居触,返回match對象
re.findall() 搜索字符串,列表類型返回全部匹配子串
re.split() 將一個字符串按照正則表達(dá)式匹配結(jié)果進(jìn)行分割老赤,返回列表類型
re.finditer() 搜索字符串轮洋,返回匹配的迭代類型,每個迭代元素都是match對象
re.sub() 在一個字符串中替換所有匹配子串抬旺,返回替換后字符串
  • re.search(pattern, string, flags=0)

    pattern:正則表達(dá)式的字符串或原生字符串表示

    string:待匹配字符串

    flags:正則表達(dá)式使用時控制標(biāo)記

    標(biāo)記有一下幾種

    常用標(biāo)記 說明
    re.I re.IGNORECASE h忽略正則表達(dá)式的大小寫弊予,[A-Z]能夠匹配小寫字符
    re.M re.MULTILINE 正則表達(dá)式中的^操作符能夠?qū)⒔o定字符串的每行當(dāng)作匹配開始
    re.S re.DOTALL 正則表達(dá)式中的.操作符能夠匹配所有字符,默認(rèn)的匹配是除換行外的所有字符
  • re.match(pattern, string, flags=0)

  • re.findall(pattern, string, flags=0)

  • re.split(pattern, string, maxsplit=0, flags=0)

    maxsplit:最大分割數(shù)嚷狞,剩余部分作為最后一個元素輸出

  • re.finditer(pattern, string, flags=0)

  • re.sub(pattern, repl, string, count=0, flags=0)

    repl:替換匹配字符串的字符串

    count:匹配最大替換次數(shù)

Re庫的另一個等價用法

? 除了函數(shù)式用法外块促,還有一種面向?qū)ο蟮挠梅ㄈ傺撸聪染幾g出正則表達(dá)式對象床未,在對這個對象進(jìn)行操作

其中生成編譯對象的用法為

regex = re.complie(pattern, flags=0)

regex為編譯后生成的正則表達(dá)式對象,pattern為正則字符串或原生字符串表示振坚。flags為正則表達(dá)式使用時的控制標(biāo)記薇搁。通過調(diào)用regex對象(也可以是其他名字)的方法實現(xiàn)搜索,具體方法的名字與上面功能函數(shù)相同渡八。

上面很多的函數(shù)都返回了match對象啃洋,接下來簡單介紹match對象

Match對象介紹

Match對象是一次匹配的結(jié)果传货,包含很多匹配的信息

Match對象的屬性

屬性 說明
.string 待匹配的文本
.re 匹配時使用的pttern對象(正則表達(dá)式)
.pos 正則表達(dá)式搜索文本的開始位置
.endpos 正則表達(dá)式的結(jié)束位置

Match對象的方法

方法 說明
.group(0) 獲得匹配后的字符串
.start() 匹配字符串在原始字符串的開始位置
.end() 匹配字符串在原始字符串的結(jié)束位置
.span() 返回(開始位置,結(jié)束位置)

以"BIT100081 TSU10084"為例

>>> import re
>>> m = re.search(r'[1-9]\d{5}', "BIT100081 TSU100084")
>>> m.string
'BIT100081 TSU100084'
>>> m.re
re.complie('[1-9]\\d{5}')    #正則表達(dá)式對象
>>> m.endpos
19
>>> m.group(0)
'100081'
>>> m.start()
3
>>> m.end()
9
>>> m.span()
(3, 9)

Re庫的貪婪匹配和最小匹配

Re庫默認(rèn)采用貪婪匹配, 即輸出匹配最長的子串,如果要輸出最短的字符串,則采用最小匹配,在匹配符后加上?

最小匹配操作符

操作符 說明
*? 前一個字符0次或無限次擴(kuò)展,最小匹配
+? 前一個字符1次或無限次擴(kuò)展,最小匹配
?? 前一個字符0次或1次擴(kuò)展,最小匹配
{m,n}? 擴(kuò)展前一個字符m至n次,最小匹配
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宏娄,隨后出現(xiàn)的幾起案子问裕,更是在濱河造成了極大的恐慌,老刑警劉巖孵坚,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粮宛,死亡現(xiàn)場離奇詭異,居然都是意外死亡卖宠,警方通過查閱死者的電腦和手機(jī)巍杈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扛伍,“玉大人筷畦,你說我怎么就攤上這事〈倘鳎” “怎么了鳖宾?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長作媚。 經(jīng)常有香客問我攘滩,道長,這世上最難降的妖魔是什么纸泡? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任漂问,我火速辦了婚禮,結(jié)果婚禮上女揭,老公的妹妹穿的比我還像新娘蚤假。我一直安慰自己,他們只是感情好吧兔,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布磷仰。 她就那樣靜靜地躺著,像睡著了一般境蔼。 火紅的嫁衣襯著肌膚如雪灶平。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天箍土,我揣著相機(jī)與錄音逢享,去河邊找鬼。 笑死吴藻,一個胖子當(dāng)著我的面吹牛瞒爬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼侧但,長吁一口氣:“原來是場噩夢啊……” “哼矢空!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起禀横,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤屁药,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后柏锄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體者祖,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年绢彤,在試婚紗的時候發(fā)現(xiàn)自己被綠了七问。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡茫舶,死狀恐怖械巡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饶氏,我是刑警寧澤讥耗,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站疹启,受9級特大地震影響鲜滩,放射性物質(zhì)發(fā)生泄漏唉铜。R本人自食惡果不足惜违帆,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一豪直、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荤懂,春花似錦茁裙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至廊宪,卻和暖如春矾瘾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箭启。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工壕翩, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人册烈。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓戈泼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赏僧。 傳聞我的和親對象是個殘疾皇子大猛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容