15K程式設計師做的小玩意,就是一個爬蟲案例,但是為啥我看完也會了!

weixin_33895657發表於2017-07-25

今天在逛論壇,一位程式設計師分享了他的成果,相對來說比較簡單。所以我效仿著去試了一下,確實挺好玩,講的是多執行緒。所以今天給大家分享一下,對爬蟲有興趣的小夥伴可以好好學習一下。再給大家分享之前呢,這裡推薦下我自己建的學習交流群:148715490,不管你是小白還是大牛,小編我都挺歡迎,不定期分享乾貨,包括2017最新的企業案例學習資料和零基礎入門教程,歡迎初學和進階中的小夥伴。

案例:多執行緒爬蟲

目標:待爬取頁面URL:http://www.codingke.com/course/248?Invite_code=294973

要求:

使用requests獲取頁面資訊,用XPATH/re 做資料提取

獲取每個帖子裡的 使用者頭像連結、使用者主頁、使用者名稱、使用者性別、使用者年齡、段子內容、點贊次數、評論次數

儲存到本地json檔案內

採用多執行緒

queue(佇列物件)

queue是python中的標準庫,可以直接import queue引用,佇列是執行緒間最常用的交換資料的形式

python下多執行緒:

對於資源,加鎖是個重要的環節。因為python原生的list, dict等,都是not thread safe的。而queue,是thread safe(執行緒案例)的,因此在滿足使用條件下,建議使用佇列

初始化:class queue.Queue(maxsize) FIFO(先進先出)

常用方法:

queue.Queue.qsize() 返回佇列的大小

queue.Queue.empty() 如果佇列為空,返回True,反之返回False

queue.Queue.full() 如果佇列滿了,返回True,反之返回False

queue.Queue.get([block[, timeout]]) 從佇列中取出一個值,timeout為等待時間

建立一個“佇列”物件

import queue

myqueue = queue.Queue(maxsize = 10)

將一個值放入佇列中

myqueue.put(10)

將一個值從佇列中取出

myqueue.get()

程式碼方面小編有點懶。所以很粗糙,各位老鐵輕點噴。小編玻璃心啊。站在我的角度考慮考慮問題啊。

#!/usr/bin/python3

# -*- conding:utf-8 -*-

__author__='mayi'

"""

案例:多執行緒爬蟲

目標:待爬取頁面首頁URL:http://www.codingke.com/course/248?Invite_code=294973

要求:

1.使用requests獲取頁面資訊,用XPATH/re 做資料提取

2.獲取每個帖子裡的 使用者頭像連結、使用者主頁、使用者名稱、使用者性別、使用者年齡、段子內容、點贊次數、評論次數

3.儲存到json檔案內

4.採用多執行緒

"""

importrequests

fromlxmlimportetree

fromqueueimportQueue

importthreading

importtime

importjson

# 資料佇列

data_queue=Queue()

exitFlag_Parser=False

# 鎖

lock=threading.Lock()

classThreadCrawl(threading.Thread):

"""

爬取執行緒類

"""

def__init__(self, thread_name, page_queue):

threading.Thread.__init__(self)

self.thread_name=thread_name

self.page_queue=page_queue

self.url="http://www.qiushibaike.com/8hr/page/"

self.header={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36'}

defrun(self):

print(self.thread_name+" Starting...")

self.qiushi_spider()

print(self.thread_name+" Exiting...")

defqiushi_spider(self):

globaldata_queue

whileTrue:

# page佇列為空時,迴圈結束

ifself.page_queue.empty():

break

else:

page=self.page_queue.get()

full_url=self.url+str(page)+"/"

print(full_url)

# 多次嘗試傳送請求失敗後結束、防止死迴圈

timeout=5

whiletimeout:

try:

# 防止訪問太快

time.sleep(1)

content=requests.get(full_url, headers=self.header)

data_queue.put(content.text)

break

exceptException as e:

print(e)

timeout-=1

time.sleep(1)

classThreadParser(threading.Thread):

"""

頁面解析類

"""

def__init__(self, thread_name, file_name):

threading.Thread.__init__(self)

self.thread_name=thread_name

self.file_name=file_name

defrun(self):

# 開始

print(self.thread_name+" Starting...")

globaldata_queue, exitFlag_Parser

whilenotexitFlag_Parser:

try:

item=data_queue.get(block=False)

ifitem:

self.parse_data(item)

data_queue.task_done()

except:

pass

# 結束

print(self.thread_name+" Exiting...")

defparse_data(self, item):

"""

解析網頁函式

:param item: 網頁內容

"""

globallock

try:

html=etree.HTML(item)

# id = qiushi_tag_119336220:id均包含:qiushi_tag_

result=html.xpath('//div[contains(@id,"qiushi_tag_")]')

forresinresult:

try:

# 使用者頭像連結、使用者主頁、使用者名稱、使用者性別、使用者年齡、段子內容、點贊次數、評論次數

# 使用者頭像連結

head_url=res.xpath('.//img/@src')[0]

# 使用者主頁

home_url="http://www.qiushibaike.com"+res.xpath('.//a/@href')[0]

# 使用者名稱

user_name=res.xpath('.//h2')[0].text

# 使用者性別:匿名使用者,匹配不到性別

article_gender=res.xpath('./div/div/@class')

ifarticle_gender:

gender=article_gender[0].split()[-1].replace("Icon", "")

else:

gender=""

# 使用者年齡:匿名使用者,匹配不到年齡

article_age=res.xpath('./div/div')

ifarticle_age:

age=article_age[0].text

else:

age=0

# 段子內容

content=res.xpath('.//div[@class="content"]/span')[0].text.strip()

# 點贊次數

stats_vote=res.xpath('.//span[@class="stats-vote"]//i[@class="number"]')

ifstats_vote:

stats_vote=stats_vote[0].text.strip()

else:

stats_vote="0"

# 評論次數

stats_comments=res.xpath('.//span[@class="stats-comments"]//i[@class="number"]')

ifstats_comments:

stats_comments=stats_comments[0].text.strip()

else:

stats_comments="0"

record={

"head_url": head_url,

"home_url": home_url,

"user_name": user_name,

"gender": gender,

"age": age,

"content": content,

"stats_vote": stats_vote,

"stats_comments": stats_comments

}

with lock:

self.file_name.write(json.dumps(record, ensure_ascii=False)+",")

exceptException as e:

print(e)

exceptException as e:

print(e)

defmain():

"""

主函式

:return:

"""

# 採集的資料儲存在本地磁碟的檔名

file_name=open("糗事百科.json","a", encoding="utf-8")

# 待採集的起始頁碼

start_page=int(input("請輸入起始頁碼:"))

# 待採集的終止頁碼

end_page=int(input("請輸入終止頁碼:"))

# 定義一個page佇列

pageQueue=Queue()

forpageinrange(start_page, end_page+1):

# 頁碼入佇列

pageQueue.put(page)

# 初始化採集執行緒

crawl_threads=[]

crawl_list=["採集執行緒1","採集執行緒2","採集執行緒3"]

forthread_nameincrawl_list:

thread=ThreadCrawl(thread_name, pageQueue)

thread.start()

crawl_threads.append(thread)

# 初始化解析執行緒

parser_threads=[]

parser_list=["解析執行緒1","解析執行緒2","解析執行緒3"]

forthread_nameinparser_list:

thread=ThreadParser(thread_name, file_name)

thread.start()

parser_threads.append(thread)

# 等待列隊被清空

whilenotpageQueue.empty():

pass

# 等待所有執行緒處理完成

forthreadincrawl_threads:

thread.join()

# 等待佇列被清空

whilenotdata_queue.empty():

pass

# 通知執行緒退出

globalexitFlag_Parser

exitFlag_Parser=True

forthreadinparser_threads:

thread.join()

with lock:

file_name.close()

if__name__=='__main__':

# 執行主函式

main()

相關文章