前言:有些時(shí)候,我們?cè)诓橐恍┍容^老的數(shù)據(jù)窥摄,由于網(wǎng)站升級(jí)镶奉,或者改版之類的,我們無(wú)法得到我們想要數(shù)據(jù)的網(wǎng)址崭放,也就無(wú)法爬取數(shù)據(jù)哨苛,下面,我將帶大家一起來(lái)分析一下自己的案例币砂。
明確需求
背景:在網(wǎng)上搜自己的名字建峭,居然找到一份文檔,大概是這樣的樣子2015年普通高校招生錄取名單公示(五) (sxkszx.cn)
然后就順騰摸瓜决摧,想找到全部的數(shù)據(jù)——2015年全部的數(shù)據(jù)查找數(shù)據(jù)
剛開始亿蒸,由于找到了網(wǎng)站,2015年普通高校招生錄取名單公示(五) (sxkszx.cn)掌桩,網(wǎng)址是http://www.sxkszx.cn/
边锁,我想在網(wǎng)站搜索,但是發(fā)現(xiàn)網(wǎng)站沒(méi)有搜索功能波岛,而且茅坛,更要命的是,這個(gè)每一頁(yè)则拷,沒(méi)有上一頁(yè)贡蓖,下一頁(yè)的鏈接,這樣我們就找不到相關(guān)聯(lián)的數(shù)據(jù)煌茬。
而且斥铺,我們發(fā)現(xiàn),網(wǎng)址的拼接也是很奇怪宣旱,http://www.sxkszx.cn/news/2015722/n82552675.html仅父,news代表新聞叛薯,2015722代表發(fā)布日期,后面的n82552675就很奇怪笙纤,如果改成n82552676則會(huì)404耗溜,也就是無(wú)法單純的修改最后一位來(lái)完成地址的模擬-
使用搜索引擎增強(qiáng)搜索力度
現(xiàn)在進(jìn)入了困境,我們無(wú)法在該網(wǎng)站查找省容。不過(guò)我們可是使用
2015年普通高校招生錄取名單 site:sxkszx.cn
來(lái)進(jìn)行網(wǎng)站的查找抖拴,這樣是使用搜索引擎來(lái)進(jìn)行該網(wǎng)站的搜索,在百度地址欄輸入搜索即可腥椒。
然后我們會(huì)找到
image.png
很多數(shù)據(jù)被找到了阿宅,然后我們分析一下,發(fā)現(xiàn)其中缺少了一些公示笼蛛,這意味這數(shù)據(jù)不太完整洒放。 -
網(wǎng)站地址分析 + 試探
現(xiàn)在問(wèn)題又回到了原點(diǎn),我們不知道網(wǎng)頁(yè)的網(wǎng)址滨砍,那么就無(wú)法爬取數(shù)據(jù)往湿。但是,現(xiàn)在要比最開始好很多惋戏,因?yàn)楝F(xiàn)在除了錄取名單五以外领追,還多了很多,以供我們分析响逢。- 整理一下绒窑,來(lái)分析一下數(shù)據(jù)的異同
為了能夠看出其中的異同,我把數(shù)據(jù)一行一行列了出來(lái)舔亭,然后行號(hào)就是公示名單的數(shù)字標(biāo)號(hào)(空缺部分表示目前未知的網(wǎng)站鏈接)
現(xiàn)在分析一下網(wǎng)址些膨,我們發(fā)現(xiàn),最后四位數(shù)字好像是遞增的
對(duì)钦铺,這有點(diǎn)像是傀蓉,新聞在數(shù)據(jù)庫(kù)中存儲(chǔ)的編號(hào)id,意味著這是第2669篇新聞职抡,那么葬燎,第2670篇新聞的網(wǎng)址應(yīng)該是怎樣的呢?
推測(cè)如下
http://www.sxkszx.cn/news/日期/n四位不知名數(shù)字2670
且日期應(yīng)當(dāng)是在2015.7.22當(dāng)天缚甩,或者之后
講道理谱净,我們這里應(yīng)該要分析 四位不知名數(shù)字 的含義,或者討論它是如何生成的擅威,但是筆者想了很多壕探,都沒(méi)能猜到,之前猜測(cè)或者與時(shí)間/文章名字/等等郊丛,但是都不太對(duì)李请,總之沒(méi)能猜到瞧筛。
- 由于我們沒(méi)猜到,所以我們有一個(gè)最樸素的想法导盅,我們遍歷0000-9999不就可以了嗎较幌?找到那個(gè)可以正確返回的鏈接
對(duì),這個(gè)想法很正確白翻。
下面的這一段代碼乍炉,返回了一個(gè)列表,列表中包含了從0000-9999的所有可能的網(wǎng)址鏈接
def gene_urls():
base_url = "http://www.sxkszx.cn/news/"
date = "2015820"
newsId = "2745"
urls = []
for i in range(1, 10000):
s = str(i).zfill(4)
url = base_url + date + "/n" + s + newsId + ".html"
urls.append(url)
return urls
寫到這里滤馍,大家可能覺(jué)得岛琼,這不很簡(jiǎn)單嗎?直接
for url in urls:
response = requests.get(url)
if response.status.code == 200:
print url
break
但是巢株,這是很樸素的單線程槐瑞,我們做一個(gè)計(jì)算
我們大概空缺的是
日期 新聞id
722 2675
723 2676
...
83
...
2696
我們發(fā)現(xiàn),一次嘗試阁苞,都要花費(fèi)很長(zhǎng)的時(shí)間随珠,如果我們想要全部試探,需要很長(zhǎng)的時(shí)間猬错,且,requests要不斷斷開茸歧,建立連接倦炒,花銷很大。
下面软瞎,我們嘗試多線程逢唤。
下面是一份很長(zhǎng)的代碼,大家不用關(guān)心具體怎么實(shí)現(xiàn)的涤浇,只要知道
single_thread 代表單線程
multi_thread 代表多線程
即可
# 不斷嘗試url鳖藕,當(dāng)返回不是404,則加入my_urls.txt中
import requests
import threading
import requests.adapters
import time
base_url = "http://www.sxkszx.cn/news/"
date = "2015722"
newsId = "2678"
# f = open("my_urls.txt", 'w+')
flag = 0
# count = 0
def gene_urls():
urls = []
for i in range(1, 10000):
s = str(i).zfill(4)
url = base_url + date + "/n" + s + newsId + ".html"
urls.append(url)
return urls
def test_url(url):
# response = requests.get(url=url, timeout=30)
# if response.status_code == 200:
# print(url)
# try:
# with requests.get(url, timeout=5) as r:
# if r.status_code == 200:
# flag = 1
# print(url)
# except:
# print("處理異常中...")
# time.sleep(5)
global flag
if flag == 0:
response = None
global count
try:
# 設(shè)置重連次數(shù)
requests.adapters.DEFAULT_RETRIES = 5
s = requests.session()
# 設(shè)置連接活躍狀態(tài)為False
s.keep_alive = False
response = requests.get(url, stream=False, timeout=10)
if response.status_code == 200:
flag = 1
print(url)
# 關(guān)閉請(qǐng)求 釋放內(nèi)存
response.close()
del (response)
except Exception as indentfier:
time.sleep(5)
def single_thread(need_test_urls):
for url in need_test_urls:
global flag
if flag == 0:
test_url(url)
else:
break
def multi_thread(need_test_urls):
threads = []
for url in need_test_urls:
threads.append(
threading.Thread(target=test_url, args=(url,))
)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print("ok")
if __name__ == "__main__":
u = gene_urls()
multi_thread(u)
# single_thread(u)
但是問(wèn)題到這里還沒(méi)有結(jié)束只锭,盡管編寫上面的代碼著恩,已經(jīng)踩了很多的坑,包括 如requests未斷開蜻展;未手動(dòng)斷開(采用with 語(yǔ)句喉誊,自動(dòng)斷開會(huì)導(dǎo)致他會(huì)慢慢斷開,還是會(huì)報(bào)錯(cuò)纵顾,max tries伍茄,服務(wù)器中止...);
但是新的問(wèn)題又來(lái)了施逾,我發(fā)現(xiàn)這個(gè)代碼在有的 日期 + 新聞id上可以運(yùn)行出結(jié)果來(lái)敷矫,有的則不會(huì)例获,具體原因我也不太清楚,評(píng)論區(qū)的小伙伴有知道的曹仗,可以告訴一下我榨汤,謝謝~
- scrapy框架的使用
最后的最后,我實(shí)在受不了了整葡,我大概明白件余,應(yīng)該是并發(fā)的時(shí)候,導(dǎo)致requests的連接無(wú)法斷開遭居,或者斷開需要時(shí)間啼器,但是還沒(méi)有斷開的這個(gè)期間,新的請(qǐng)求已經(jīng)發(fā)送了俱萍,這樣就會(huì)有一部分請(qǐng)求是沒(méi)有效果的端壳,也就是一部分鏈接被卡掉了。
而這個(gè)枪蘑,是目前的我所解決不了的损谦。
于是我想著試一下scrapy,因?yàn)槲抑奥?tīng)過(guò)scrapy岳颇,但是沒(méi)有用過(guò)照捡,想著用它來(lái)解決并發(fā)問(wèn)題,會(huì)不會(huì)好點(diǎn)话侧。
先說(shuō)實(shí)驗(yàn)結(jié)果栗精,最終成功解決了并發(fā)問(wèn)題,對(duì)于每一個(gè) 日期 + 新聞id 所產(chǎn)生的鏈接列表瞻鹏,大概在1mins內(nèi)可以得到結(jié)果悲立,已經(jīng)算是很快了。
再說(shuō)下去新博,就要講scrapy框架了薪夕,但是現(xiàn)在我好餓,我先去吃個(gè)飯飯赫悄,寶寶餓了