Python爬蟲——多線程爬取陽光問政

正所謂否副,民生無小事,今日多關(guān)注曲尸,今天我們利用多線程來爬取陽光問政,關(guān)注一下老百姓需要解決什么問題鸦列。

線程

什么是線程

線程是輕量級進程,是操作系統(tǒng)能夠進行運算調(diào)度的最小單位,它被包涵在進程之中诲锹,是進程中的實際運作單位。

其生命周期可以分為五個狀態(tài)——新建捻浦、就緒、運行盗扒、阻塞、終止缕碎,如下圖所示:

  • 新建狀態(tài):新創(chuàng)建的線程在調(diào)用 start() 方法之前赊抖,不會得到執(zhí)行檐薯;

  • 就緒狀態(tài):新建狀態(tài)的線程調(diào)用 start() 方法后,該線程就轉(zhuǎn)換到就緒狀態(tài),當獲取到CPU資源就可以執(zhí)行;

  • 運行狀態(tài):就緒狀態(tài)的線程得到了 CPU資源举户,并開始執(zhí)行 target 參數(shù)執(zhí)行的目標函數(shù)或者 run() 方法;

  • 阻塞狀態(tài):當 CPU 對多個線程進行調(diào)度時拐云,對于獲得 CPU 調(diào)度卻沒有執(zhí)行完畢的線程,該線程就進入阻塞狀態(tài)薇缅;

  • 終止狀態(tài):線程執(zhí)行結(jié)束、發(fā)生異常(Exception)或錯誤(Error)泻骤,線程就會進入終止狀態(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來獲取當前系統(tǒng)時間,由于程序執(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

首先導入線程模塊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)建了兩個線程,首先通過實例化線程類myThread()并傳入線程名,調(diào)用start()方法使線程處于就緒狀態(tài)喷鸽,再調(diào)用join()方法等待線程結(jié)束。

運行結(jié)果如下所示:

爬前分析

首先進入陽光問政最新問政網(wǎng)頁并打開開發(fā)者模式,如下圖所示:

這個網(wǎng)站比較簡單,網(wǎng)頁的URL鏈接最后一個數(shù)字就是它的頁碼,所以我們構(gòu)造url鏈接時,可以這樣:

for i in range(1,6):
    url=f'http://wzzdg.sun0769.com/political/index/politicsNewest?id=1&page={i}'

這樣就可以獲取多頁數(shù)據(jù)了,在源代碼中也有我們想要的詳情網(wǎng)頁url鏈接洪乍,其存放在上圖的右邊紅框中。

隨機打開一個問政問題并打開開發(fā)者模式巷波,如下圖所示:

可以發(fā)現(xiàn)該url為http://wzzdg.sun0769.com/political/politics/index?id=529559荤傲,對比上上圖的詳情網(wǎng)頁url终佛,只需要在獲取到的url鏈接前面添加http://wzzdg.sun0769即可绍豁。

詳情網(wǎng)頁也很簡單,我們想要的數(shù)據(jù)在源代碼中也有,所以我們待會只要獲取URL鏈接頁面的源代碼即可獲取到所有數(shù)據(jù)晶衷。

實戰(zhàn)演練

在實戰(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_).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ò)請求,并通過parsel.Selector()方法來解析響應(yīng)的文本數(shù)據(jù),最后將獲取到的url鏈接傳入到自定義get_data()方法。

注意:這里我們把url鏈接保存在一個txt文本中,方便我們在多線程里使用所有詳情網(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('編號:','')
    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()方法獲取游標驳概,再通過.execute()方法執(zhí)行單條的sql語句,執(zhí)行成功后返回受影響的行數(shù)稚照,然后關(guān)閉數(shù)據(jù)庫連接。當保存的數(shù)據(jù)不成功弱恒,就調(diào)用rollback()方法锈玉,撤消當前事務(wù)中所做的所有更改,并釋放此連接對象當前使用的任何數(shù)據(jù)庫鎖。

啟動程序

好了穷蛹,主要代碼已經(jīng)寫好了,接下來編寫啟動程序的代碼,主要代碼如下圖所示:

if __name__ == '__main__':
    t1=time.time()
    get_link()
    t2=time.time()
    print(t2-t1)

這里我們通過time.time()方法來獲取爬蟲程序的執(zhí)行時間励烦。

運行結(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)

首先導入單線程爬蟲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個線程,運行結(jié)果如下所示:

開啟十個線程來爬取數(shù)據(jù),一共用了2.4秒萎胰,大大提高了爬蟲效率。

好了,多線程爬取陽光問政就講到這里了搓扯,感謝觀看!;豢伞!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末套媚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慎皱,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異些阅,居然都是意外死亡赖钞,警方通過查閱死者的電腦和手機弓千,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門姻政,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人食绿,你說我怎么就攤上這事〔簦” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵芦拿,是天一觀的道長。 經(jīng)常有香客問我芳撒,道長笔刹,這世上最難降的妖魔是什么亦镶? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任绊起,我火速辦了婚禮虱歪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘实蔽。我一直安慰自己荡碾,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布局装。 她就那樣靜靜地躺著坛吁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铐尚。 梳的紋絲不亂的頭發(fā)上拨脉,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音宣增,去河邊找鬼玫膀。 笑死爹脾,一個胖子當著我的面吹牛解阅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倾哺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起檬某,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤喧半,失蹤者是張志新(化名)和其女友劉穎浩村,沒想到半個月后瘫筐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體依许,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年颊乘,在試婚紗的時候發(fā)現(xiàn)自己被綠了檩小。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡乍楚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渠概,到底是詐尸還是另有隱情,我是刑警寧澤寒跳,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布誓琼,位于F島的核電站,受9級特大地震影響乒省,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜机断,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望来累。 院中可真熱鬧瘤运,春花似錦冷溃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罢吃。三九已至,卻和暖如春渡冻,著一層夾襖步出監(jiān)牢的瞬間切平,已是汗流浹背定枷。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梢夯。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像坝锰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

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