送書 | 聊聊陽光問政
大家好,我是啃書君!
正所謂,民生無小事,今日多關(guān)注,今天我們利用多線程來爬取陽光問政,關(guān)注一下老百姓需要解決什么問題。
線程什么是線程
線程是輕量級(jí)進(jìn)程,是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位,它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。
其生命周期可以分為五個(gè)狀態(tài)——新建、就緒、運(yùn)行、阻塞、終止,如下圖所示:
- 新建狀態(tài):新創(chuàng)建的線程在調(diào)用 start() 方法之前,不會(huì)得到執(zhí)行;
- 就緒狀態(tài):新建狀態(tài)的線程調(diào)用 start() 方法后,該線程就轉(zhuǎn)換到就緒狀態(tài),當(dāng)獲取到CPU資源就可以執(zhí)行;
- 運(yùn)行狀態(tài):就緒狀態(tài)的線程得到了 CPU資源,并開始執(zhí)行 target 參數(shù)執(zhí)行的目標(biāo)函數(shù)或者 run() 方法;
- 阻塞狀態(tài):當(dāng) CPU 對(duì)多個(gè)線程進(jìn)行調(diào)度時(shí),對(duì)于獲得 CPU 調(diào)度卻沒有執(zhí)行完畢的線程,該線程就進(jìn)入阻塞狀態(tài);
- 終止?fàn)顟B(tài):線程執(zhí)行結(jié)束、發(fā)生異常(Exception)或錯(cuò)誤(Error),線程就會(huì)進(jìn)入終止?fàn)顟B(tài)。
線程創(chuàng)建
創(chuàng)建線程可以分為五步:編寫執(zhí)行程序、創(chuàng)建線程類、在線程類run方法中調(diào)用要執(zhí)行的程序、開啟線程和等待線程結(jié)束。
編寫執(zhí)行程序
import time
def print_time(threadName):
for i in range(5):
time.sleep(0.5)
timestamp = time.time()
date = time.localtime(timestamp)
Now_date = time.strftime('%Y-%m-%d %H:%M:%S', date)
print ("%s: %s" % (threadName, Now_date))
在執(zhí)行程序中,我們通過time來獲取當(dāng)前系統(tǒng)時(shí)間,由于程序執(zhí)行速度是很快的,所以我們通過time.sleep()方法讓程序休眠0.5秒,這樣我們就可以看到線程的交替執(zhí)行。
創(chuàng)建線程類
import threading
class myThread (threading.Thread):
def __init__(self, name):
super(myThread,self).__init__()
self.name = name
def run(self):
pass
首先導(dǎo)入線程模塊threading,創(chuàng)建myThread()線程類并通過threading.Thread來繼承線程屬性,調(diào)用super()方法并初始化name變量。
run()方法
def run(self):
print ("開始線程:" + self.name)
print_time(self.name)
print ("退出線程:" + self.name)
在run方法中,我們通過調(diào)用print_time()方法并傳入self.name參數(shù)來執(zhí)行第一步編寫的執(zhí)行程序。
開啟、等待線程
線程類和執(zhí)行程序都寫好了,接下來開啟線程并等待線程結(jié)束,具體代碼如下所示:
thread1 = myThread("Thread-1")
thread2 = myThread("Thread-2")
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("退出主線程")
這里我們創(chuàng)建了兩個(gè)線程,首先通過實(shí)例化線程類myThread()并傳入線程名,調(diào)用start()方法使線程處于就緒狀態(tài),再調(diào)用join()方法等待線程結(jié)束。
運(yùn)行結(jié)果如下所示:
實(shí)戰(zhàn)分析
首先進(jìn)入陽光問政最新問政網(wǎng)頁并打開開發(fā)者模式,如下圖所示:
這個(gè)網(wǎng)站比較簡(jiǎn)單,網(wǎng)頁的URL鏈接最后一個(gè)數(shù)字就是它的頁碼,所以我們構(gòu)造url鏈接時(shí),可以這樣:
for i in range(1,6):
url=f'http://wzzdg.sun0769.com/political/index/politicsNewest?id=1&page={i}'
這樣就可以獲取多頁數(shù)據(jù)了,在源代碼中也有我們想要的詳情網(wǎng)頁url鏈接,其存放在上圖的右邊紅框中。
隨機(jī)打開一個(gè)問政問題并打開開發(fā)者模式,如下圖所示:
可以發(fā)現(xiàn)該url為http://wzzdg.sun0769.com/political/politics/index?id=529559,對(duì)比上上圖的詳情網(wǎng)頁url,只需要在獲取到的url鏈接前面添加http://wzzdg.sun0769即可。
詳情網(wǎng)頁也很簡(jiǎn)單,我們想要的數(shù)據(jù)在源代碼中也有,所以我們待會(huì)只要獲取URL鏈接頁面的源代碼即可獲取到所有數(shù)據(jù)。
實(shí)戰(zhàn)演練在實(shí)戰(zhàn)演練中,我們首先通過編寫單線程爬蟲來爬取陽光問政的數(shù)據(jù),再通過多線程程序執(zhí)行單線程爬蟲。
單線程爬蟲
獲取詳情網(wǎng)頁url
首先獲取詳情網(wǎng)頁的url鏈接,主要代碼如下所示:
import requests
import parsel
import pymysql
def get_link():
for i in range(1,6):
url=f'http://wzzdg.sun0769.com/political/index/politicsNewest?id=1&page={i}'
response=requests.get(url,headers=headers)
Xapth=parsel.Selector(response.text)
f = open('url.txt', 'a', encoding='utf-8')
ul_list = Xapth.xpath('//html/body/div[2]/div[3]/ul[2]/li')
for li in ul_list:
url_href='https://wzzdg.sun0769.com/'+li.xpath('./span[3]/a/@href').extract_first()
f.write(url_href)
f.write('\n')
get_data(url_href)
我們一共獲取5頁數(shù)據(jù),每頁數(shù)據(jù)有15條詳情網(wǎng)頁的URL鏈接,通過requests.get()方法發(fā)出網(wǎng)絡(luò)請(qǐng)求,并通過parsel.Selector()方法來解析響應(yīng)的文本數(shù)據(jù),最后將獲取到的url鏈接傳入到自定義get_data()方法。
注意:這里我們把url鏈接保存在一個(gè)txt文本中,方便我們?cè)诙嗑€程里使用所有詳情網(wǎng)頁的url鏈接。
獲取詳情網(wǎng)頁數(shù)據(jù)
獲取詳情網(wǎng)頁的url后,接下來就獲取其內(nèi)容了,具體代碼如下所示:
def get_data(i):
response=requests.get(i,headers=headers)
Xapth=parsel.Selector(response.text)
data={}
data['number_id']=Xapth.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[4]/text()').extract_first().replace('編號(hào):','')
data['state_now']=Xapth.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[3]/text()').extract_first().replace('狀態(tài):','').strip()
data['PoliticalTitle']=Xapth.xpath('/html/body/div[3]/div[2]/div[2]/p/text()').extract_first()
data['PoliticalTime']=Xapth.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[2]/text()').extract_first().replace('發(fā)布日期','')
data['url_href']=i
data['text']=Xapth.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first().replace('\n','').replace('\r','')
saving_scenery_data(list(data.values()))
獲取詳情網(wǎng)頁的數(shù)據(jù)和獲取url鏈接代碼差不多,這里我就不一一解釋了,最后我們把數(shù)據(jù)傳遞到自定義方法saving_sunshine_data()中。
保存數(shù)據(jù)
這次數(shù)據(jù)我們保存在MySQL數(shù)據(jù)庫中,主要代碼如下圖所示:
def saving_scenery_data(srr):
db = pymysql.connect(host=host, user=user, password=passwd, port=port, db='Politics')
cursor = db.cursor()
sql = 'insert into problem_data(number_id, state_now, PoliticalTitle, PoliticalTime, url_href,text) values(%s,%s,%s,%s,%s,%s)'
try:
cursor.execute(sql,srr)
db.commit()
except:
db.rollback()
db.close()
首先連接數(shù)據(jù)庫,通過cursor()方法獲取游標(biāo),再通過.execute()方法執(zhí)行單條的sql語句,執(zhí)行成功后返回受影響的行數(shù),然后關(guān)閉數(shù)據(jù)庫連接。當(dāng)保存的數(shù)據(jù)不成功,就調(diào)用rollback()方法,撤消當(dāng)前事務(wù)中所做的所有更改,并釋放此連接對(duì)象當(dāng)前使用的任何數(shù)據(jù)庫鎖。
啟動(dòng)程序
好了,主要代碼已經(jīng)寫好了,接下來編寫啟動(dòng)程序的代碼,主要代碼如下圖所示:
if __name__ == '__main__':
t1=time.time()
get_link()
t2=time.time()
print(t2-t1)
這里我們通過time.time()方法來獲取爬蟲程序的執(zhí)行時(shí)間。
運(yùn)行結(jié)果如下圖所示:
從結(jié)果來看,單線程爬取數(shù)據(jù)用了16秒,接下來編寫多線程來爬取數(shù)據(jù)。
多線程爬蟲
剛才單線程爬蟲的文件名為yangguang.py,可以直接調(diào)用單線程爬蟲方法來編寫多線程爬蟲,首先創(chuàng)建多線程爬蟲類,主要代碼如下所示:
import yangguang
import threading
import time
f=open('url.txt',mode='r')
class mythread(threading.Thread):
def __init__(self,f):
super(mythread,self).__init__()
self.f=f
def run(self)->None:
for i in self.f:
yangguang.get_data(i)
首先導(dǎo)入單線程爬蟲yangguang.py文件,打開剛才單線程爬蟲保存的txt文件,再創(chuàng)建mythread()類并初始化線程類,重寫run()方法,通過for循環(huán)把txt文件中的url讀取并傳遞在單線程爬蟲yangguang.get_data()方法中。
好了,多線程類寫好了,接下來編寫執(zhí)行代碼,主要代碼如下所示:
if __name__ == '__main__':
t1=time.time()
yangguang.create_db()
threads = [mythread(f) for i in range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
t2=time.time()
print(t2-t1)
這里我們創(chuàng)建了10個(gè)線程,運(yùn)行結(jié)果如下所示:
開啟十個(gè)線程來爬取數(shù)據(jù),一共用了2.4秒,大大提高了爬蟲效率。
好了,多線程爬取陽光問政就講到這里了,感謝觀看?。。?/p>
又到了每周三的送書時(shí)刻,今天給大家?guī)淼氖恰?span style="font-weight:700;">菜鳥輕松拿offer:軟件測(cè)試工程師面試秘笈》,本書以測(cè)試基礎(chǔ)知識(shí)為主,非常適合應(yīng)屆畢業(yè)生、初入門軟件測(cè)試的人員。對(duì)于轉(zhuǎn)行進(jìn)入測(cè)試行業(yè)的人員,本書也非常適合。本書也可用于教材,指導(dǎo)測(cè)試人員找工作,與面試官進(jìn)行交流。作為一本參考書,也適合面試官閱讀,以便于對(duì)求職者進(jìn)行篩選。
本書特色
實(shí)用性,接地氣。總結(jié)真實(shí)小例,引出實(shí)用技巧,幫助讀者提高面試成功率。融入百位測(cè)試人員的面試總結(jié)。示例豐富,每一個(gè)面試點(diǎn)都有對(duì)應(yīng)的示例。對(duì)立角度解析,從面試者和面試官兩方面對(duì)問題進(jìn)行解析。形色兼具,外在優(yōu)雅與內(nèi)在實(shí)力相結(jié)合,征服面試官。點(diǎn)擊閱讀原文查看本書詳情哦!
點(diǎn)擊下方回復(fù):送書 即可!
大家如果有什么建議,歡迎掃一掃二維碼私聊小編~回復(fù):加群 可加入Python技術(shù)交流群
