小豬的Python學(xué)習(xí)之旅 —— 2.爬蟲初涉
引言本節(jié)開始學(xué)習(xí)Python爬蟲,,先是介紹兩個(gè)玩到爛的兩個(gè)庫(kù)urllib和BeautifulSoup,, 然后寫了兩個(gè)很簡(jiǎn)單的小爬蟲來(lái)體驗(yàn)下爬蟲的魅力,建議直接把代碼復(fù)制粘貼一波 體驗(yàn)下,覺(jué)得有點(diǎn)意思再一點(diǎn)點(diǎn)學(xué)習(xí)~學(xué)什么東西興趣很重要,!
1.urllib模塊詳解用于操作URL的模塊(庫(kù),,py3把urllib和urllib2合并到了一起) 爬網(wǎng)頁(yè)必須掌握的最基礎(chǔ)的東東。 1) 爬取網(wǎng)頁(yè)import urllib.request
import urllib.parse
import json
# 爬取網(wǎng)頁(yè)信息
html_url = "http://www.baidu.com"
html_resp = urllib.request.urlopen(html_url)
# 讀取全部,,讀取一行可用readline(),,多行返回列表的可用readlines()
html = html_resp.read()
html = html.decode('utf-8') # 解碼
print(html)
# 獲得其他信息:
html_resp.info() # 獲得頭相關(guān)的信息,HTTPMessage對(duì)象
html_resp.getcode() # 獲得狀態(tài)碼
html_resp.geturl() # 獲取爬取的url
# url中包含漢字是不符合URL標(biāo)準(zhǔn)的,,需要進(jìn)行編碼
urllib.request.quote('http://www.baidu.com')
# 編碼后:http%3A//www.baidu.com
urllib.request.unquote('http%3A//www.baidu.com')
# 解碼后:http://www.baidu.com
2) 爬取二進(jìn)制文件(圖片,,音頻等)# 下載圖片
pic_url = "http://static./coder-pig/agr9d5uow8r5ug8iafnl6dlz/1.jpg"
pic_resp = urllib.request.urlopen(pic_url)
pic = pic_resp.read()
with open("LeiMu.jpg", "wb") as f:
f.write(pic)
# 也可以直接調(diào)用urlretrieve下載,比如下載音頻
music_url = "http://7xl4pr.com2.z0.glb./" "%E4%B8%83%E7%94%B0%E7%9C%9F%E4%B8%93%E5%8C%BA%2F%E4%" "B8%AD%E6%96%87%E8%AF%BE%2F%E6%83%B3%E8%" "B1%A1%E7%82%B9%E5%8D%A1%2F%2B6.mp3"
urllib.request.urlretrieve(music_url, "兒歌.mp3")
3) 模擬Get請(qǐng)求與Post請(qǐng)求PS:下面用到的json模塊 :用于將Python原始類型與json類型相互轉(zhuǎn)換,使用 如果是讀取文件可以用:dump()和load()方法,,字符串的話用下述兩個(gè): dumps()編碼 [Python -> Json] dict => object list, tuple => array str => string True => true int, float, int- & float-derived Enums => number False => false None => null
loads()解碼 [Json -> Python] object => dict array => list string => str number (int) => int number(real) => float true =>True false => False null => None
# 模擬Get
get_url = "http:///api/data/" + urllib.request.quote("福利") + "/1/1"
get_resp = urllib.request.urlopen(get_url)
get_result = json.loads(get_resp.read().decode('utf-8'))
# 這里后面的參數(shù)用于格式化Json輸出格式
get_result_format = json.dumps(get_result, indent=2,
sort_keys=True, ensure_ascii=False)
print(get_result_format)
# 模擬Post
post_url = "http://.login"
phone = "13555555555"
password = "111111"
values = {
'phone': phone,
'password': password
}
data = urllib.parse.urlencode(values).encode(encoding='utf-8')
req = urllib.request.Request(post_url, data)
resp = urllib.request.urlopen(req)
result = json.loads(resp.read()) # Byte結(jié)果轉(zhuǎn)Json
print(json.dumps(result, sort_keys=True,
indent=2, ensure_ascii=False)) # 格式化輸出Json
4) 修改請(qǐng)求頭有些網(wǎng)站為了避免別人使用爬蟲惡意采取信息會(huì)進(jìn)行一些反爬蟲的操作,, 比如通過(guò)請(qǐng)求頭里的User-Agent,檢查訪問(wèn)來(lái)源是否為正常的訪問(wèn)途徑,, 我們可以修改請(qǐng)求頭來(lái)進(jìn)行模擬正常的訪問(wèn),。Request中有個(gè)headers參數(shù), 有兩種方法進(jìn)行設(shè)置: 1.把請(qǐng)求頭都塞到字典里,,實(shí)例化Request對(duì)象的時(shí)候傳入 2.通過(guò)Request對(duì)象的add_header()方法一個(gè)個(gè)添加 # 修改頭信息
novel_url = "http://www./1_1496/"
headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) '
'AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/63.0.3239.84 Safari/537.36',
'Referer': 'http://www.baidu.com',
'Connection': 'keep-alive'}
novel_req = urllib.request.Request(novel_url, headers=headers)
novel_resp = urllib.request.urlopen(novel_req)
print(novel_resp.read().decode('gbk'))
5) 設(shè)置連接超時(shí)# urlopen函數(shù)時(shí)添加timeout參數(shù),,單位是秒
urllib.request.urlopen(novel_req, timeout=20)
6) 延遲提交數(shù)據(jù)一般服務(wù)器會(huì)對(duì)請(qǐng)求的IP進(jìn)行記錄,如果單位時(shí)間里訪問(wèn)的次數(shù)達(dá)到一個(gè)閥值,, 會(huì)認(rèn)為該IP地址是爬蟲,,會(huì)彈出驗(yàn)證碼驗(yàn)證或者直接對(duì)IP進(jìn)行封禁。一個(gè)最簡(jiǎn)單 的方法就是延遲每次提交的時(shí)間,,直接用time模塊 的sleep(秒) 函數(shù)休眠下,。 7) 代理對(duì)應(yīng)限制ip訪問(wèn)速度的情況我們可以使用延遲提交數(shù)據(jù)的做法, 但是有些是限制訪問(wèn)次數(shù)的,,同一個(gè)ip只能在一段時(shí)間里訪問(wèn) 多少次這樣,,而且休眠這種方法效率也是挺低的。更好的方案是 使用代理,,通過(guò)代理ip輪換去訪問(wèn)目標(biāo)網(wǎng)址,。 用法示例如下: # 使用ip代理
ip_query_url = "http://www."
# 1.創(chuàng)建代理處理器,ProxyHandler參數(shù)是一個(gè)字典{類型:代理ip:端口}
proxy_support = urllib.request.ProxyHandler({'http': '221.214.110.130:8080'})
# 2.定制,,創(chuàng)建一個(gè)opener
opener = urllib.request.build_opener(proxy_support)
# 3.安裝opener
urllib.request.install_opener(opener)
headers = {
'User-Agent': 'User-Agent:Mozilla/5.0 (X11; Linux x86_64)'
' AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/63.0.3239.84 Safari/537.36',
'Host': 'www.'
}
MAX_NUM = 10 # 有時(shí)網(wǎng)絡(luò)堵塞,,會(huì)報(bào)URLError錯(cuò)誤,所以加一個(gè)循環(huán)
request = urllib.request.Request(ip_query_url, headers=headers)
for i in range(MAX_NUM):
try:
response = urllib.request.urlopen(request, timeout=20)
html = response.read()
print(html.decode('utf-8'))
break
except:
if i < MAX_NUM - 1:
continue
else:
print("urllib.error.URLError: <urlopen error timed out>")
輸出結(jié)果: 如圖代理成功,,這里的網(wǎng)站是用于查詢請(qǐng)求ip的,,另外,我們一般會(huì)弄一個(gè) 代理ip的列表,,然后每次隨機(jī)的從里面取出一個(gè)來(lái)使用。 8) CookieCookie定義:指某些網(wǎng)站為了辨別用戶身份、進(jìn)行 session 跟蹤而儲(chǔ)存在用戶本地終端上的數(shù)據(jù)(通常經(jīng)過(guò)加密) 比如有些頁(yè)面你在登錄前是無(wú)法訪問(wèn)的,,你登錄成功會(huì)給你分配 Cookie,,然后你帶著Cookie去請(qǐng)求頁(yè)面才能正常訪問(wèn)。 使用http.cookiejar這個(gè)模塊可以幫我們獲取Cookie,,實(shí)現(xiàn)模擬登錄,。 該模塊的主要對(duì)象(父類->子類): CookieJar –> FileCookieJar –>MozillaCookieJar與LWPCookieJar 因?yàn)闀簳r(shí)沒(méi)找到合適例子,就只記下關(guān)鍵代碼,,后續(xù)有再改下例子: # ============ 獲得Cookie ============
# 1.實(shí)例化CookieJar對(duì)象
cookie = cookiejar.CookieJar()
# 2.創(chuàng)建Cookie處理器
handler = urllib.request.HTTPCookieProcessor(cookie)
# 3.通過(guò)CookieHandler創(chuàng)建opener
opener = urllib.request.build_opener(handler)
# 4.打開網(wǎng)頁(yè)
resp = opener.open("http://www.")
for i in cookie:
print("Name = %s" % i.name)
print("Name = %s" % i.value)
# ============ 保存Cookie到文件 ============
# 1.用于保存cookie的文件
cookie_file = "cookie.txt"
# 2.創(chuàng)建MozillaCookieJar對(duì)象保存Cookie
cookie = cookiejar.MozillaCookieJar(cookie_file)
# 3.創(chuàng)建Cookie處理器
handler = urllib.request.HTTPCookieProcessor(cookie)
# 4.通過(guò)CookieHandler創(chuàng)建opener
opener = urllib.request.build_opener(handler)
# 5.打開網(wǎng)頁(yè)
resp = opener.open("http://www.baidu.com")
# 6.保存Cookie到文件中,,參數(shù)依次是:
# ignore_discard:即使cookies將被丟棄也將它保存下來(lái)
# ignore_expires:如果在該文件中cookies已存在,覆蓋原文件寫入
cookie.save(ignore_discard=True, ignore_expires=True)
# ============ 讀取Cookie文件 ============
cookie_file = "cookie.txt"
# 1.創(chuàng)建MozillaCookieJar對(duì)象保存Cookie
cookie = cookiejar.MozillaCookieJar(cookie_file)
# 2.從文件中讀取cookie內(nèi)容
cookie.load(cookie_file, ignore_expires=True, ignore_discard=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
resp = opener.open("http://www.baidu.com")
print(resp.read().decode('utf-8'))
2.Beautiful Soup 庫(kù)詳解1) 官方介紹:一個(gè)可以從 HTML 或 XML 文件中提取數(shù)據(jù)的 Python庫(kù),,它能夠通過(guò) 你喜歡的轉(zhuǎn)換器實(shí)現(xiàn)慣用的文檔導(dǎo)航,、查找、修改文檔的方式,。 Beautiful Soup 會(huì)幫你節(jié)省數(shù)小時(shí)甚至數(shù)天的工作時(shí)間,。 簡(jiǎn)單點(diǎn)說(shuō)就是 爬取HTML和XML的利器 2) 安裝庫(kù)PyCharm直接安裝: File -> Default Settings -> Project Interpreter 選擇Python 3的版本 -> 點(diǎn)+號(hào) -> 搜索beautifulsoup4 安裝即可 pip方法安裝的自己行百度,或者看上一節(jié)的內(nèi)容~ 3) 實(shí)例化BeautifulSoup對(duì)象簡(jiǎn)單點(diǎn)說(shuō)這一步就是把html丟到BeautifulSoup對(duì)象里,,可以是請(qǐng)求后的網(wǎng)站,, 也可以是本地的HTML文件,第二個(gè)參數(shù)是指定解析器,,html.parser是內(nèi)置的 html解析器,,你還可以用lxml.parser,不過(guò)要另外導(dǎo)庫(kù),。 網(wǎng)站:soup = BeautifulSoup(resp.read(), ‘html.parser’) 本地:soup = BeautifulSoup(open(‘index.html’),,’html.parser’) 另外還可以調(diào)用soup.prettify() 格式化輸出HTML 4) 四大對(duì)象一.Tag(標(biāo)簽) 查找的是:在所有內(nèi)容中第一個(gè)符合要求的標(biāo)簽,最常用的兩個(gè)東東:
tag.name :獲得tag的名字,,比如body
tag.attrs :獲取標(biāo)簽內(nèi)所有屬性,,返回一個(gè)字典,可以根據(jù)鍵取值,,也可以 直接調(diào)用get(‘xxx’)拿到屬性,。 還有個(gè)玩法是:可以soup.body.div.div.a 這樣玩,同過(guò)加標(biāo)簽名的形式 輕松獲取標(biāo)簽的內(nèi)容,,不過(guò)查找的只是第一個(gè),!基本沒(méi)什么卵用… 二.NavigableString(內(nèi)部文字) 獲取標(biāo)簽內(nèi)部的文字,直接調(diào)用.string 三.BeautifulSoup(文檔的全部?jī)?nèi)容) 當(dāng)做一個(gè)Tag對(duì)象就好,,只是可以分別獲取它的類型,,名稱,一級(jí)屬性而已 四.Comment(特殊的NavigableString) 這種對(duì)象調(diào)用.string來(lái)輸出內(nèi)容會(huì)把注釋符號(hào)去掉,,直接把注釋里的內(nèi)容 打出來(lái),,需要加一波判斷: if type(soup.a.string)==bs4.element.Comment:
print soup.a.string
5) 各種節(jié)點(diǎn)PS:感覺(jué)作用不大,,用得不多,還是記錄下方法,,可以跳過(guò)~ 子節(jié)點(diǎn)與子孫節(jié)點(diǎn): contents:把標(biāo)簽下的所有子標(biāo)簽存入到列表,,返回列表 children:和contents一樣,但是返回的不是一個(gè)列表而是 一個(gè)迭代器,,只能通過(guò)循環(huán)的方式獲取信息,,類型是:list_iterator 前兩者僅包含tag的直接子節(jié)點(diǎn),如果是想扒出子孫節(jié)點(diǎn),,可以使用descendants 會(huì)把所有節(jié)點(diǎn)都剝離出來(lái),,生成一個(gè)生成器對(duì)象 6) 文檔樹搜索最常用的方法: find_all (self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs):
name參數(shù):通過(guò)html標(biāo)簽名直接搜索,會(huì)自動(dòng)忽略字符串對(duì)象,, 參數(shù)可以是:字符串,,正則表達(dá)式,列表,,True或者自定義方法 keyword參數(shù):通過(guò)html標(biāo)簽的id,,href(a標(biāo)簽)和title,class要寫成class_,, 可以同時(shí)過(guò)濾多個(gè),,對(duì)于不能用的tags屬性,可以直接用一個(gè)attrs字典包著,, 比如:find_all(attrs={‘data-foo’: ‘value’} text:搜索文檔中的字符串內(nèi)容 limit:限制返回的結(jié)果數(shù)量 recursive:是否遞歸檢索所有子孫節(jié)點(diǎn)
其他方法: find (self, name=None, attrs={}, recursive=True, text=None, **kwargs): 和find_all作用一樣,,只是返回的不是列表,而是直接返回結(jié)果,。
find_parents() 和find_parent() : find_all() 和 find() 只搜索當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn),孫子節(jié)點(diǎn)等. find_parents() 和 find_parent()用來(lái)搜索當(dāng)前節(jié)點(diǎn)的父輩節(jié)點(diǎn), 搜索方法與普通tag的搜索方法相同,搜索文檔搜索文檔包含的內(nèi)容,。
find_next_sibling() 和find_next_siblings() : 這2個(gè)方法通過(guò) .next_siblings 屬性對(duì)當(dāng) tag 的所有后面解析的兄弟 tag 節(jié)點(diǎn)進(jìn)行迭代, find_next_siblings() 方法返回所有符合條件的后面 的兄弟節(jié)點(diǎn),find_next_sibling() 只返回符合條件的后面的第一個(gè)tag節(jié)點(diǎn)。
find_previous_siblings() 和find_previous_sibling() : 這2個(gè)方法通過(guò) .previous_siblings 屬性對(duì)當(dāng)前 tag 的前面解析的兄弟 tag 節(jié)點(diǎn)進(jìn)行迭代, find_previous_siblings()方法返回所有符合條件的前面的 兄弟節(jié)點(diǎn), find_previous_sibling() 方法返回第一個(gè)符合條件的前面的兄弟節(jié)點(diǎn),。
find_all_next() 和find_next() : 這2個(gè)方法通過(guò) .next_elements 屬性對(duì)當(dāng)前 tag 的之后的 tag 和字符串進(jìn)行 迭代, find_all_next() 方法返回所有符合條件的節(jié)點(diǎn), find_next() 方法返回 第一個(gè)符合條件的節(jié)點(diǎn),。
find_all_previous() 和find_previous() 這2個(gè)方法通過(guò) .previous_elements 屬性對(duì)當(dāng)前節(jié)點(diǎn)前面的 tag 和字符串 進(jìn)行迭代, find_all_previous() 方法返回所有符合條件的節(jié)點(diǎn), find_previous()方法返回第一個(gè)符合條件的節(jié)點(diǎn)
3.爬蟲實(shí)戰(zhàn)1) 小說(shuō)網(wǎng)站數(shù)據(jù)抓取下載txt到本地上一節(jié)學(xué)習(xí)了基礎(chǔ)知識(shí),這節(jié)又學(xué)了簡(jiǎn)單的爬蟲姿勢(shì):urllib和Beautiful Soup庫(kù),, 肯定是要來(lái)個(gè)實(shí)戰(zhàn)練練手的,,選了個(gè)最簡(jiǎn)單的而且有點(diǎn)卵用的小東西玩玩。 相信各位大佬平時(shí)都有看網(wǎng)絡(luò)小說(shuō)的習(xí)慣吧,,應(yīng)該很少有老司機(jī)去會(huì)起點(diǎn) 付費(fèi)看,,一般都有些盜版小說(shuō)網(wǎng)站,比如:筆趣看:http://www./ 手機(jī)在線看,,廣告是可怕的,,想想你在擠滿人的地跌上,突然蹦出一對(duì) 柰子,,你的第一反應(yīng)肯定是關(guān)掉,,然而還是naive,,點(diǎn)X直接彈一個(gè)新的 網(wǎng)頁(yè)什么壯陽(yáng)延時(shí)…尷尬得一匹。學(xué)了Python爬蟲的東西了,,寫個(gè)小爬蟲 爬爬小說(shuō)順道練練手豈不美滋滋,。不說(shuō)那么多,開搞: 我最近在看的小說(shuō):唐家三少的《斗羅大陸3-龍王傳說(shuō)》 http://www./1_1496/ 圖中圈住的部分就是小說(shuō)的章節(jié),,F(xiàn)12打開chrome的開發(fā)者工具,切到Network選項(xiàng)卡 (或者直接看Elements也行),,選Response,,刷新一波可以看到這樣的HTML結(jié)構(gòu): 當(dāng)然我們要找的內(nèi)容不在這里,繼續(xù)往下翻: listmain這個(gè)全局搜了下,,是唯一的(好吧,,找這個(gè)真的太沒(méi)難度了…) 直接find_all(attrs={‘class’: ‘listmain’}) 就可以拿到這段東西了 find_all返回一個(gè)bs4.element.ResultSet 對(duì)象,for循環(huán)遍歷一波 這個(gè)對(duì)象,,(迭代對(duì)象的類型是:bs4.element.Tag)打印一波可以看到: 我們要留下的只是<a>xxx</a> 這種東西,,可以在循環(huán)的時(shí)候順帶把 無(wú)關(guān)的篩選掉: 可以打印下這個(gè)a_list,剩下的全是<a>xxx</a> 因?yàn)樽钚抡鹿?jié)列表那里默認(rèn)有12個(gè),,我們應(yīng)該從楔子那里開始,, 所以把把a(bǔ)_list列表分下片:result_list = a_list[12:] 過(guò)濾掉前面的最新章節(jié)部分~ 章節(jié)部分的數(shù)據(jù)就處理完畢了,有章節(jié)內(nèi)容的url,,以及章節(jié)的名稱,, 接著我們來(lái)看看章節(jié)頁(yè)面的內(nèi)容結(jié)構(gòu),隨便打開一個(gè): 比如:http://www./1_1496/450364.html 就不說(shuō)了,,class=”showtxt”又是唯一的,,直接: showtxt = chapter_soup.find_all(attrs={‘class’: ‘showtxt’}) 把showtxt循環(huán)打印一波,里面的東西就是我們想要的東東了~ url有了,,章節(jié)名有了,,內(nèi)容有了,是時(shí)候?qū)懭氲轿募锪?,這個(gè) 過(guò)于簡(jiǎn)單就不用多說(shuō)了,,strip=True代表刪除字符前后的所有空格: 到此我們爬區(qū)小說(shuō)的小爬蟲就寫完了,不過(guò)有個(gè)小問(wèn)題是,,批量 快速訪問(wèn)的時(shí)候,,會(huì)報(bào)503異常,因?yàn)榉?wù)器一般會(huì)對(duì)限制ip在 一段時(shí)間里訪問(wèn)的頻次,,上面也講了要么休眠,,要么搞ip代理, 肯定是搞ip代理嗨一些,,接著我們來(lái)寫一個(gè)爬蟲來(lái)抓取西刺 代理的ip,,并校驗(yàn)是否可用,,然后存到本地,我們的代理ip池,, 哈哈~ 附上小說(shuō)抓取這部分的代碼: from bs4 import BeautifulSoup
import urllib.request
from urllib import error
novel_url = "http://www./1_1496/" # 小說(shuō)頁(yè)面地址
base_url = "http://www." # 根地址,,用于拼接
save_dir = "Novel/" # 下載小說(shuō)的存放路徑
# 保存小說(shuō)到本地
def save_chapter(txt, path):
try:
with open(path, "a+") as f:
f.write(txt.get_text(strip=True))
except (error.HTTPError, OSError) as reason:
print(str(reason))
else:
print("下載完成:" + path)
# 獲得所有章節(jié)的url
def get_chapter_url():
chapter_req = urllib.request.Request(novel_url)
chapter_resp = urllib.request.urlopen(chapter_req, timeout=20)
chapter_content = chapter_resp.read()
chapter_soup = BeautifulSoup(chapter_content, 'html.parser')
# 取出章節(jié)部分
listmain = chapter_soup.find_all(attrs={'class': 'listmain'})
a_list = [] # 存放小說(shuō)所有的a標(biāo)簽
# 過(guò)濾掉不是a標(biāo)簽的數(shù)據(jù)
for i in listmain:
if 'a' not in str(i):
continue
for d in i.findAll('a'):
a_list.append(d)
# 過(guò)濾掉前面"最新章節(jié)列表"部分
result_list = a_list[12:]
return result_list
# 獲取章節(jié)內(nèi)容并下載
def get_chapter_content(c):
chapter_url = base_url + c.attrs.get('href') # 獲取url
chapter_name = c.string # 獲取章節(jié)名稱
chapter_req = urllib.request.Request(chapter_url)
chapter_resp = urllib.request.urlopen(chapter_req, timeout=20)
chapter_content = chapter_resp.read()
chapter_soup = BeautifulSoup(chapter_content, 'html.parser')
# 查找章節(jié)部分內(nèi)容
showtxt = chapter_soup.find_all(attrs={'class': 'showtxt'})
for txt in showtxt:
save_chapter(txt, save_dir + chapter_name + ".txt")
if __name__ == '__main__':
novel_list = get_chapter_url()
for chapter in novel_list:
get_chapter_content(chapter)
2) 抓取西刺代理ip并校驗(yàn)是否可用之前就說(shuō)過(guò)了,很多服務(wù)器都會(huì)限制ip訪問(wèn)的頻度或者次數(shù),,可以通過(guò)設(shè)置代理 ip的方式來(lái)解決這個(gè)問(wèn)題,,代理ip百度一搜一堆,最出名的應(yīng)該是西刺代理了: http://www./ 爬蟲中代理ip使用得非常頻繁,,每次都打開這個(gè)頁(yè)面粘貼復(fù)制,,感覺(jué) 過(guò)于低端,而且還有個(gè)問(wèn)題,,代理ip是會(huì)過(guò)期失效的,,而且不一定 一直可以用:要不來(lái)個(gè)這樣的騷操作: 寫個(gè)爬蟲爬取代理ip列表,然后校驗(yàn)是否可用,,把可用的存在本地,, 下次需要代理的時(shí)候,讀取這個(gè)文件中的ip,,放到一個(gè)列表中,,然后 輪流切換ip或者通過(guò)random模塊隨機(jī)取出一個(gè),去訪問(wèn)目標(biāo)地址,。 抓取的網(wǎng)頁(yè)是:http://www./nn/1 Network選項(xiàng)卡,,Response看下頁(yè)面結(jié)構(gòu),這里我喜歡在PyCharm上 新建一個(gè)HTML文件,,結(jié)點(diǎn)可折疊,,找關(guān)鍵位置代碼很方便: 如圖,不難發(fā)現(xiàn)就是我們要找的內(nèi)容,,可以從 find_all(attrs={‘id’: ‘ip_list’}) 這里入手,,或者find_all(‘tr’),這里有個(gè)小細(xì)節(jié)的地方首項(xiàng)是類似于表頭 的東西,,我們可以通過(guò)列表分片去掉第一個(gè):find_all(‘tr’)[1:] 此時(shí)的列表: 接著就是要拿出ip和端口了,,遍歷,先拿td,,然后根據(jù)游標(biāo)拿數(shù)據(jù): 數(shù)據(jù)都拼接成”ip:端口號(hào)”的形式了,,然后就是驗(yàn)證這個(gè)列表里的代理 是否都可以用了,驗(yàn)證方法也很簡(jiǎn)單,,直接設(shè)置代理然后訪問(wèn)百度,, 淘寶之類的網(wǎng)站,看返回碼是否為200,,是的話就代表代理可用,,追加 到可用列表中,,最后再把可用列表寫入到文件中: 然后你就有自己的代理ip池了,要用的時(shí)候讀取下文件就好,,沒(méi)事更新一波文件~ 有了代理ip池,,和上面扒小說(shuō)的程序可用結(jié)合一波,應(yīng)該就不會(huì)出現(xiàn)503的問(wèn)題了,, 具體有興趣的自行去完善吧(我懶…) 附上完整代碼: from bs4 import BeautifulSoup
import urllib.request
from urllib import error
test_url = "https://www.baidu.com/" # 測(cè)試ip是否可用
proxy_url = "http://www./nn/1" # ip抓取源
ip_file = "availableIP.txt"
# 把ip寫入到文件中
def write_file(available_list):
try:
with open(ip_file, "w+") as f:
for available_ip in available_list:
f.write(available_ip + "\n")
except OSError as reason:
print(str(reason))
# 檢測(cè)代理ip是否可用,,返回可用代理ip列表
def test_ip(test_list):
available_ip_list = []
for test in test_list:
proxy = {'http': test}
try:
handler = urllib.request.ProxyHandler(proxy)
opener = urllib.request.build_opener(handler)
urllib.request.install_opener(opener)
test_resp = urllib.request.urlopen(test_url)
if test_resp.getcode() == 200:
available_ip_list.append(test)
except error.HTTPError as reason:
print(str(reason))
return available_ip_list
# 抓取西刺代理ip
def catch_ip():
ip_list = []
try:
# 要設(shè)置請(qǐng)求頭,不然503
headers = {
'Host': 'www.',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
' (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'
}
req = urllib.request.Request(proxy_url, headers=headers)
resp = urllib.request.urlopen(req, timeout=20)
content = resp.read()
soup = BeautifulSoup(content, 'html.parser')
catch_list = soup.find_all('tr')[1:]
# 保存代理ip
for i in catch_list:
td = i.find_all('td')
ip_list.append(td[1].get_text() + ":" + td[2].get_text())
return ip_list
except urllib.error.URLError as reason:
print(str(reason))
if __name__ == "__main__":
xici_ip_list = catch_ip()
available_ip_list = test_ip(xici_ip_list)
write_file(available_ip_list)
結(jié)語(yǔ): 本節(jié)學(xué)習(xí)了urllib庫(kù)與Beautiful Soup,,并通過(guò)兩個(gè)非常簡(jiǎn)單的程序體驗(yàn)了 一波爬蟲的編寫,,順道溫習(xí)了下上節(jié)的基礎(chǔ)知識(shí),美滋滋,,比起Android 天天寫界面,有趣太多,,正如基神所言,,是時(shí)候找個(gè)新方向?qū)W習(xí)了~ 本節(jié)的東西還是小兒科,下節(jié)我們來(lái)啃硬骨頭正則表達(dá)式,!
本節(jié)參考文獻(xiàn):
|