虎撲《不冷笑話》爬蟲實戰,順帶說說最近學寫爬蟲的經歷(一)
前言
樓主16年畢業,工作內容目前主要以ETL開發為主。有一段時間逛知乎,經常看到有人分享一些資料分析的文章,比如美團上的點餐分析、豆瓣的評分分析,還有網易雲音樂熱評分析之類的。感覺這些文章都是有實打實的東西在,而且作者們的文筆也深得我心。後來也知道,這些資料大部分是用python爬蟲爬取的,所以我也開始斷斷續續在看相關的內容。本帖就是我在做其中一部分實戰的心路歷程了,權當記錄一下,水平較低莫怪。
1.起因
虎撲是廣大jrs的家園,步行街是這個家園裡最繁華的地段。據稱廣大jrs平均學歷985,步行街街薪30w起步。 大學時經舍友安利,開始瞭解虎撲,主要是看看NBA的一些資訊。 偶爾也上上這個破街,看看jrs虐虐狗,說說家長裡短等等,別的不說,jr們的三觀都是特別正的。 《不冷笑話》基本是我每天必看的帖子,感覺《不冷笑話》的幾位老夥計非常敬業,每天都會有高質量的輸出,帖子下的熱帖也很給力,福利滿滿。 正學python,突發奇想想把《不冷笑話》的圖都爬下來。
另外隨著我學習內容的增加,一個爬蟲也由最簡單的流水線式的結構,變為模組化的設計思路,之後又變為使用成熟的框架。下面就切入正題了。
2.經過
版本1:模組化的簡單實現
其實python的上手非常簡單,原因有兩個,python語言就很簡潔,python的包非常豐富。我在B站上跟著小甲魚的視訊沒學個十幾節課,就開始對照著別人最簡單的爬蟲例子在做了。 新手無非就是requests + BeautifulSoup,一個負責HTTP請求,一個負責HTML解析。就小規模爬蟲來講,主要時間還是用來定位爬取目標在頁面中的位置。所以,先看一下最low的爬蟲是什麼樣子。
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
import os, time
import re
link = r'https://www.qiushibaike.com/pic/'
r = requests.get(link)
x = 1
soup = BeautifulSoup(r.text,'lxml')
for content in soup.find_all(class_ = 'thumb'):
xpath = content.find('img').get('src')
image = requests.get('http:' + xpath)
open('qiubai %d.jpg' % x,'wb').write(image.content)
x += 1
除去匯入包和編碼宣告,十行程式碼就能寫一個爬蟲,用來爬取糗事百科網站上的圖片,並儲存成本地檔案。
實際上,一開始要準備爬取《不冷笑話》時,帖子地址的獲取是一個非常困擾我的問題。後來經過我長期觀察發現,《不冷笑話》總是在 虎撲NBA 的固定位置出現,所以我只要在這個頁面定位好就可以進去了。
一開始我也是用最簡單的結構去做《不冷笑話》的爬蟲,但是又覺得這樣做好像是重用很低,所以,按照模組化的思路,劃定了幾個功能點:
- 1、定位不冷笑話在首頁的位置,獲取連結和標題
- 2、建立以標題命名的目錄,如果目錄存在,說明已下載,程式結束
- 3、進入不冷笑話的介面,獲取正文中的圖片連結,存入列表
- 4、獲取亮貼中的圖片連結,存入列表
- 5、儲存圖片,根據傳入引數為正文或評論進行命名,區分圖片來源
- 6、大功告成
最終基本都按照了功能劃分了模組,寫出來的是這個樣子的。
#-*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
import os, time
import re
url = (r'https://nba.hupu.com/')
#獲取不冷笑話在首頁的位置,返回url和標題
def get_buleng_title_url(url):
index_html = requests.get(url)
index_html_s = BeautifulSoup(index_html.text,'lxml')
main_street = index_html_s.find(class_ = 'gray-list main-stem max250')
url_list = []
url_name_list = []
for dd in main_street.find_all('dd',limit = 5):
url_list.append(dd.a.get('href'))
url_name_list.append(dd.a.get_text())
return [url_list[4],url_name_list[4]]
#獲取不冷笑話正文中的圖片列表,利用set去重
def get_pic_url(buleng_list):
pic_url_list = set()
buleng_html = requests.get(buleng_list[0])
buleng_html_s = BeautifulSoup(buleng_html.text,'lxml')
buleng_content = buleng_html_s.find(class_='quote-content')
for pic_url in buleng_content.find_all('img'):
try:
original_url = pic_url.get('data-original')
pic_url_list.add(original_url.split('?')[0])
except:
pic_url_list.add(pic_url.get('src'))
return pic_url_list
#建立以標題命名的資料夾,並返回是否建立成功
def makedir(buleng_list):
path = ('E:\\pic\\buleng\\%s' % buleng_list[1])
if os.path.exists(path):
return 0
else:
os.makedirs(path)
return path
#獲取亮貼中的圖片列表,set去重
def get_comment_pic_url(buleng_list):
comment_pic_url_list = set()
buleng_html = requests.get(buleng_list[0])
buleng_html_s = BeautifulSoup(buleng_html.text,'lxml')
buleng_comment = buleng_html_s.find(id='readfloor')
for floor in buleng_comment.find_all('table'):
for pic_url in floor.find_all('img'):
try:
original_url = pic_url.get('data-original')
comment_pic_url_list.add(original_url.split('?')[0])
except:
comment_pic_url_list.add(pic_url.get('src'))
return comment_pic_url_list
#下載圖片,可下載gif、jpg、png格式
def download_pic(pic_url_list,path,pic_from = '正文'):
a = 1
for url in pic_url_list :
if url.endswith('.gif'):
pic = requests.get(url)
with open((path+('\\%s-%s.gif' % (pic_from,a))),'wb') as f:
f.write(pic.content)
f.close
print('下載一張%s動圖' % pic_from)
a += 1
if url.endswith('.jpg'):
pic = requests.get(url)
with open((path+('\\%s-%s.jpg' % (pic_from,a))),'wb') as f:
f.write(pic.content)
f.close
print('下載一張%sjpg圖' % pic_from)
a +=1
if url.endswith('.png'):
pic = requests.get(url)
with open((path+('\\%s-%s.png' % (pic_from,a))),'wb') as f:
f.write(pic.content)
f.close
print('下載一張%spng圖' % pic_from)
a +=1
if __name__ == "__main__":
buleng = get_buleng_title_url(url)
path = makedir(buleng)
if path != 0:
pic_url_list = get_pic_url(buleng)
comment_pic_url_list = get_comment_pic_url(buleng)
download_pic(pic_url_list,path)
download_pic(comment_pic_url_list,path,'評論')
else:
print('目錄已存在,等待虎撲更新')
版本2:模組化的簡單實現plus
後來我一想,反正都是獲取了主幹道所有的推薦貼,為啥不自定義要下載的帖子呢,這樣的話,這十幾個推薦貼想下哪個下哪個,不是功能更上一層樓了? 多虧了模組化的設計,博主略一沉吟,部分修改如下:
#獲取熱帖在首頁的位置,返回url和標題
def get_retie_title_url(url):
index_html = requests.get(url)
index_html_s = BeautifulSoup(index_html.text,'lxml')
main_street = index_html_s.find(class_ = 'gray-list main-stem max250')
url_list = []
url_name_list = []
for dd in main_street.find_all('dd',limit = 14):
url_list.append(dd.a.get('href'))
url_name_list.append(dd.a.get_text())
return [url_list,url_name_list]
#return [url_list[4],url_name_list[4]] 原始碼對比
#獲取熱帖正文中的圖片列表,利用set去重
def get_pic_url(retie_list,num): #新加num引數
pic_url_list = set()
retie_html = requests.get(retie_list[0][num-1])
retie_html_s = BeautifulSoup(retie_html.text,'lxml')
retie_content = retie_html_s.find(class_='quote-content')
for pic_url in retie_content.find_all('img'):
try:
original_url = pic_url.get('data-original')
pic_url_list.add(original_url.split('?')[0])
except:
pic_url_list.add(pic_url.get('src'))
return pic_url_list
if __name__ == "__main__":
retie = get_retie_title_url(url)
a = 1
for retie_title in retie[1]:
print(a,' ',retie_title)
print('--------------------------------')
a += 1
num = int(input('請輸入要下載的熱帖排序(1-14)')) #獲取輸入序號
if num < 1 or num > 14:
print('不合法')
else:
path = makedir(retie[1][num-1])
if path != 0:
pic_url_list = get_pic_url(retie,num)
comment_pic_url_list = get_comment_pic_url(retie,num)
download_pic(pic_url_list,path)
download_pic(comment_pic_url_list,path,'評論')
else:
print('目錄已存在,等待虎撲更新')
然後效果就是這樣啦
3.總結
好了,今天的無干貨流水賬就先告一段落,欲知後事如何,且聽下回分解