五年P(guān)ython爬蟲程序員整理的全棧爬蟲知識點

1 最簡單的單頁面抓取

思路:

  • 獲取頁面所有url
  • 對獲取的所有url進(jìn)行分類
  • A 獲取屬于本域名下的url
  • B 獲取屬于其他url

2 用到的模塊

  • urllib
  • bs4
  • re正則表達(dá)式
五年P(guān)ython爬蟲程序員整理的全棧爬蟲知識點

學(xué)習(xí)Python中的小伙伴幔烛,需要學(xué)習(xí)資料的話顺少,可以到我的微信公眾號:Python學(xué)習(xí)知識圈,后臺回復(fù):“01”驰坊,即可拿Python學(xué)習(xí)資料


3 代碼說明:

<pre style="-webkit-tap-highlight-color: transparent; box-sizing: border-box; font-family: Consolas, Menlo, Courier, monospace; font-size: 16px; white-space: pre-wrap; position: relative; line-height: 1.5; color: rgb(153, 153, 153); margin: 1em 0px; padding: 12px 10px; background: rgb(244, 245, 246); border: 1px solid rgb(232, 232, 232); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import urllib
from bs4 import BeautifulSoup
import re
def get_all_url(url):
urls = []
web = urllib.urlopen(url)#使用urllib模塊的urlopen函數(shù)打開url嗡载,復(fù)制給web
soup =BeautifulSoup(web.read())#將web內(nèi)容轉(zhuǎn)化為beautigulsoup格式的數(shù)據(jù)窑多。

通過正則過濾合理的url(針對與freebuf.com來講)

tags_a =soup.findAll(name='a',attrs={'href':re.compile("^https?://")})

soup.findall函數(shù)的運用,配合正則表達(dá)式來過濾url

try :
for tag_a in tags_a:

re:^ 表示匹配字符串開頭例如洼滚,^ abc 匹配 abc

埂息? 表示匹配前一個字符串0次或1次,例如 abc遥巴? 匹配 ab 和 abc

return urls

except:
pass
return urls

得到所有freebuf.com下的url

def get_local_urls(url):
local_urls = []
urls = get_all_url(url)
for _url in urls:
ret = _url
if 'freebuf.com' in ret.replace('//','').split('/')[0]:
local_urls.append(_url)
return local_urls

if 'freebuf.com' in ret.replace('//','').split('/')[0]:這個if語句不是很明白千康,通過split()函數(shù),把域名分割铲掐,獲取分割后組成的類表的第一個字符串拾弃。但是在分割前為什么要把//替換成空格?摆霉?豪椿?

得到所有的不是freebuf.com域名的url

def get_remote_urls(url):
remote_urls = []
urls = get_all_url(url)
for _url in urls:
ret = _url
if "freebuf.com" not in ret.replace('//','').split('/')[0]:
remote_urls.append(_url)
return remote_urls

主函數(shù)

def main():
url = 'http://freebuf.com/'
rurls = get_remote_urls(url)
print "--------------------remote urls-----------------------"
for ret in rurls:
print ret
print "---------------------localurls-----------------------"
lurls = get_local_urls(url)
for ret in lurls:
print ret
if name == 'main':
main()
</pre>

上面是單獨對一個頁面抓取。如果對整個站點抓取的話携栋,還設(shè)計到url的處理搭盾,用作者的原話:

我們可以把整站當(dāng)成一個錯綜復(fù)雜的圖結(jié)構(gòu),有一些算法基礎(chǔ)的讀者都會知道圖的簡單遍歷方法:dfs和bfs(深度優(yōu)先和廣度優(yōu)先)婉支。如果這里讀者有問題的話建議先去學(xué)習(xí)一下這兩種算法鸯隅。大體的算法結(jié)構(gòu)我們清楚了,但是在實現(xiàn)中我們顯然需要特殊處理url磅摹,需要可以區(qū)分當(dāng)前目標(biāo)站點域名下的網(wǎng)站和其他域名的網(wǎng)站滋迈,除此之外,在href的值中經(jīng)常會出現(xiàn)相對url户誓,這里也要特別處理饼灿。

下面是代碼:

<pre style="-webkit-tap-highlight-color: transparent; box-sizing: border-box; font-family: Consolas, Menlo, Courier, monospace; font-size: 16px; white-space: pre-wrap; position: relative; line-height: 1.5; color: rgb(153, 153, 153); margin: 1em 0px; padding: 12px 10px; background: rgb(244, 245, 246); border: 1px solid rgb(232, 232, 232); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import urllib
from bs4 import BeautifulSoup
import urlparse
import time
import urllib2

urllib 和 urllib2的區(qū)別http://blog.csdn.net/dolphin_h/article/details/45296353

url = "http://xxxx.xx/"
domain = "xxxx.xx"
deep = 0
tmp = ""
sites = set()
visited = set()

local = set()

集合:

python的set和其他語言類似, 是一個無序不重復(fù)元素集, 基本功能包括關(guān)系測試和消除重復(fù)元素. 集合對象還支持union(聯(lián)合), intersection(交), difference(差)和sysmmetric difference(對稱差集)等數(shù)學(xué)運算.
def get_local_pages(url,domain):
global deep
global sites
global tmp
repeat_time = 0
pages = set()

防止url讀取卡住

while True:
try:
print "Ready to Open the web!"
time.sleep(1)

time.sleep()函數(shù),

Python time sleep() 函數(shù)推遲調(diào)用線程的運行帝美,可通過參數(shù)secs指秒數(shù)碍彭,表示進(jìn)程掛起的時間
print "Opening the web", url
web = urllib2.urlopen(url=url,timeout=3)
print "Success to Open the web"
break
except:
print "Open Url Failed !!! Repeat"
time.sleep(1)
repeat_time = repeat_time+1
if repeat_time == 5:
return

上面整段判斷url能不能打開

print "Readint the web ..."
soup = BeautifulSoup(web.read())
print "..."

提取標(biāo)簽 a

for tag in tags:

避免參數(shù)傳遞異常

try:
ret = tag['href']
except:
print "Maybe not the attr : href"
continue
o = urlparse.urlparse(ret)

urlparse.urlparse函數(shù)的使用

urlparse模塊主要是把url拆分為6部分,并返回元組。并且可以把拆分后的部分再組成一個url庇忌。主要有函數(shù)有urljoin舞箍、urlsplit、urlunsplit皆疹、urlparse等疏橄。
urlparse.urlparse(urlstring[, scheme[, allow_fragments]])
將urlstring解析成6個部分,它從urlstring中取得URL略就,并返回元組 (scheme, netloc, path, parameters, query, fragment)捎迫,但是實際上是基于namedtuple,是tuple的子類表牢。它支持通過名字屬性或者索引訪問的部分URL窄绒,每個組件是一串字符,也有可能是空的崔兴。組件不能被解析為更小的部分彰导,%后面的也不會被解析,分割符號并不是解析結(jié)果的一部分敲茄,除非用斜線轉(zhuǎn)義位谋,注意,返回的這個元組非常有用堰燎,例如可以用來確定網(wǎng)絡(luò)協(xié)議(HTTP倔幼、FTP等等 )、服務(wù)器地址爽待、文件路徑,等等翩腐。
"""

Debug I/O

for _ret in o:
if _ret == "":
pass
else:
print _ret
"""

處理相對路徑url

if o[0] is "" and o[1] is "":
print "Fix Page: " +ret
url_obj = urlparse.urlparse(web.geturl())

獲取web頁面的url鸟款,用urlparse函數(shù)抽離

ret = url_obj[0] + "://" + url_obj[1] + url_obj[2] + ret

組成一個絕對url

保持url的干凈

ret = ret[:8] + ret[8:].replace('//','/')
o = urlparse.urlparse(ret)

這里不是太完善,但是可以應(yīng)付一般情況

if '../' in o[2]:
paths = o[2].split('/')
for i inrange(len(paths)):
if paths[i] == '..':
paths[i] = ''
if paths[i-1]:
paths[i-1] = ''
tmp_path = ''
for path in paths:
if path == '':
continue
tmp_path = tmp_path + '/' +path
ret =ret.replace(o[2],ret_path)
print "FixedPage: " + ret
上面整段都是判斷獲到的url是絕對url還是相對url茂卦。如果是相對url何什,則還需進(jìn)行重組成完善的絕對url。包括url中含../的情況進(jìn)行處理等龙。但是處理../的情況不理解!!???

協(xié)議處理

if 'http' not in o[0]:
print "Bad Page:" + ret.encode('ascii')
continue

url合理性檢驗

if o[0] is "" and o[1] is not "":
print "Bad Page: " +ret
continue

域名檢驗

if domain not in o[1]:

變量domain用來檢驗獲取的所有url是否是改域名下的

print "Bad Page: " +ret
continue

整理处渣,輸出

newpage = ret
if newpage not in sites:
print "Add New Page: " + newpage
pages.add(newpage)
return pages

dfs算法遍歷全站

def dfs(pages):

無法獲取新的url說明便利完成,即可結(jié)束dfs

if pages is set():
return
global url
global domain
global sites
global visited
sites = set.union(sites,pages)
for page in pages:
if page not in visited:
print "Visiting",page
visited.add(page)
url = page
pages = get_local_pages(url, domain)
dfs(pages)
print "sucess"

整段程序下來蛛砰,一直不知道 變量domain用來檢驗獲取的所有url是否是改域名下的

pages = get_local_pages(url, domain)
dfs(pages)
for i in sites:
print i
</pre>

整個的大概思路是:

  • 傳入url
  • 判斷能否打開
  • 打開url罐栈,獲取整個改url的web信息
  • 提取屬性‘href’
  • 遍歷提取到的鏈接,這里叫做url2
  • 判斷url是否為相對url
  • 是的話
  • 對其進(jìn)行抽離[urlparse.urlparse()]
  • 重組
  • (處理相對url)
  • 最后檢驗協(xié)議泥畅、url域名荠诬。
  • 帶入遍歷算法。
  • 最后是web元素的處理

兩個模塊

bs4模塊和docx模塊的使用

**bs4前面有寫

我們重點要講findAll方法的兩個參數(shù):name和attr**

Name: 指的是標(biāo)簽名,傳入一個標(biāo)簽名的名稱就可以返回所有固定名稱的標(biāo)簽名

Attr: 是一個字典存儲需要查找的標(biāo)簽參數(shù)柑贞,返回對應(yīng)的標(biāo)簽

  • Tag.children 表示獲取tag標(biāo)簽的所有子標(biāo)簽
  • Tag.string 表示獲取tag標(biāo)簽內(nèi)的所有字符串方椎,不用一層一層索引下去尋找字符串
  • Tag.attrs[key] 表示獲取tag標(biāo)簽內(nèi)參數(shù)的鍵值對鍵為key的值
  • Tag.img 表示獲取tag標(biāo)簽的標(biāo)簽名為img的自標(biāo)簽(一個)

docx模塊:

在使用這個模塊的時候,要記清楚如果:

pip install python-docx

easy_install python-docx

兩種方式安裝都不能正常使用的話钧嘶,就需要下載tar包自己手動安裝

Docx模塊是一個可以直接操作生成docx文檔的python模塊棠众,使用方法極盡簡單:

  • Demo = Document() #在內(nèi)存中建立一個doc文檔
  • Demo.add_paragraph(data) #在doc文檔中添加一個段落
  • Demo.add_picture(“pic.png”) #doc文檔中添加一個圖片
  • Demo.save(‘demo.docx’) #存儲docx文檔

觀察html結(jié)構(gòu):

五年P(guān)ython爬蟲程序員整理的全棧爬蟲知識點

我們大致觀察一下結(jié)構(gòu),定位到文章的具體內(nèi)容需要找到標(biāo)簽有决,然后再遍歷標(biāo)簽的子標(biāo)簽即可遍歷到所有的段落闸拿,配圖資料

五年P(guān)ython爬蟲程序員整理的全棧爬蟲知識點

這樣定位到圖片,那么我們怎么樣來尋找

<pre style="-webkit-tap-highlight-color: transparent; box-sizing: border-box; font-family: Consolas, Menlo, Courier, monospace; font-size: 16px; white-space: pre-wrap; position: relative; line-height: 1.5; color: rgb(153, 153, 153); margin: 1em 0px; padding: 12px 10px; background: rgb(244, 245, 246); border: 1px solid rgb(232, 232, 232); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">from docx import Document
from bs4 import BeautifulSoup
import urllib
url ="http://freebuf.com/news/94263.html"
data = urllib.urlopen(url)
document = Document()
soup = BeautifulSoup(data)
article = soup.find(name ="div",attrs={'id':'contenttxt'}).children

這段是提取我們所要的信息

for e in article:
try:
if e.img:
pic_name = ''
print e.img.attrs['src']
if 'gif' in e.img.attrs['src']:
pic_name = 'temp.gif'
elif 'png' in e.img.attrs['src']:
pic_name = 'temp.png'
elif 'jpg' in e.img.attrs['src']:
pic_name = 'temp.jpg'
else:
pic_name = 'temp.jpeg'
urllib.urlretrieve(e.img.attrs['src'], filename=pic_name)

下面我們再來看看 urllib 模塊提供的 urlretrieve() 函數(shù)疮薇。urlretrieve() 方法直接將遠(yuǎn)程數(shù)據(jù)下載到本地胸墙。

1

help(urllib.urlretrieve)

2

Help on function urlretrieve in module urllib:

3

4

urlretrieve(url, filename=None, reporthook=None, data=None)

參數(shù) finename 指定了保存本地路徑(如果參數(shù)未指定,urllib會生成一個臨時文件保存數(shù)據(jù)按咒。)

參數(shù) reporthook 是一個回調(diào)函數(shù)迟隅,當(dāng)連接上服務(wù)器、以及相應(yīng)的數(shù)據(jù)塊傳輸完畢時會觸發(fā)該回調(diào)励七,我們可以利用這個回調(diào)函數(shù)來顯示當(dāng)前的下載進(jìn)度智袭。

參數(shù) data 指 post 到服務(wù)器的數(shù)據(jù),該方法返回一個包含兩個元素的(filename, headers)元組掠抬,filename 表示保存到本地的路徑吼野,header 表示服務(wù)器的響應(yīng)頭。

document.add_picture(pic_name)
except:
pass
if e.string:
print e.string.encode('gbk','ignore')
document.add_paragraph(e.string)
document.save("freebuf_article.docx")
print "success create a document"</pre>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末两波,一起剝皮案震驚了整個濱河市瞳步,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腰奋,老刑警劉巖单起,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異劣坊,居然都是意外死亡嘀倒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門局冰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來测蘑,“玉大人,你說我怎么就攤上這事康二√几欤” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵赠摇,是天一觀的道長固逗。 經(jīng)常有香客問我浅蚪,道長,這世上最難降的妖魔是什么烫罩? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任惜傲,我火速辦了婚禮,結(jié)果婚禮上贝攒,老公的妹妹穿的比我還像新娘盗誊。我一直安慰自己,他們只是感情好隘弊,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布哈踱。 她就那樣靜靜地躺著,像睡著了一般梨熙。 火紅的嫁衣襯著肌膚如雪开镣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天咽扇,我揣著相機與錄音邪财,去河邊找鬼。 笑死质欲,一個胖子當(dāng)著我的面吹牛树埠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嘶伟,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼怎憋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了九昧?” 一聲冷哼從身側(cè)響起绊袋,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铸鹰,沒想到半個月后愤炸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡掉奄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凤薛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姓建。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖缤苫,靈堂內(nèi)的尸體忽然破棺而出速兔,到底是詐尸還是另有隱情,我是刑警寧澤活玲,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布涣狗,位于F島的核電站谍婉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏镀钓。R本人自食惡果不足惜穗熬,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丁溅。 院中可真熱鬧唤蔗,春花似錦、人聲如沸窟赏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涯穷。三九已至棍掐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拷况,已是汗流浹背作煌。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝠嘉,地道東北人最疆。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像蚤告,于是被迫代替她去往敵國和親努酸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

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