最近想把之前寫的一些代碼和經驗寫成博客記錄下來,寫了兩篇之後我發現寫博客蠻有意思的,既是對知識的一個總結,又可以提高我自己的寫作能力,更重要的是自己敞開心扉與自己的一次自我審視與交流。
爬蟲網站和爬取目標
爬蟲網站:全景網 https://www.quanjing.com/
爬取目標:根據搜索關鍵詞爬取圖片
分析過程
沒有分析過程的爬蟲都是耍流氓。
首先進入全景網首頁,F12打開谷歌開發者工具,選中network板塊,在搜索欄中輸入關鍵字如“黑人””二字,按enter鍵搜索,進入如下頁面
觀察可得知該請求即爲搜索欄發出的搜索請求,然後點擊Preview面板,
可以看到該請求將搜索結果頁面中的所有圖片的地址一併傳給了瀏覽器,所以我們只要模擬該請求即可得到我們想要的數據,注意headers裏面的信息
圖片的分頁在這裏也有,所以我們只需要在請求參數裏面帶上即可。
到此爲止爬蟲的url和數據這些都解決了,下面要做就用多線程實現爬蟲,這裏我用的是concurrent.futures模塊實現線程池,使用該線程的步驟如下:
#1、調用 ThreadPoolExecutor 類的構造器創建一個線程池。
#2、定義一個普通函數作爲線程任務。
#3、調用 ThreadPoolExecutor 對象的 submit() 方法來提交線程任務。
#4、當不想提交任何任務時,調用 ThreadPoolExecutor 對象的 shutdown() 方法來關閉線程池。
代碼
廢話不多說了,代碼裏面有註釋
import json
import urllib
import jsonpath
import requests
import uuid
from concurrent.futures import ThreadPoolExecutor
import os
'''
全景網多線程爬蟲
'''
def run(lists):
for page in range(lists[0], lists[1]+1): #dic['startNum'] dic['endNum']
params = get_params()
params['pagenum'] = page
images = request_page(get_url(), get_headers(),params)
try:
imgStr = images.replace("searchresult(", "").replace(')', '')
jsonObjs = json.loads(imgStr)
for image_url in jsonpath.jsonpath(jsonObjs,"$.imglist..imgurl"):
try:
print(lists[3] + '正在下載圖片') #dic['tname']
print('')
download_pic(image_url, str(uuid.uuid1()), lists[2]) #dic['path']
except Exception as e:
print(str(e))
continue
except Exception as e:
print(str(e))
continue
def request_page(startUrl,headers,params):
try:
requests.packages.urllib3.disable_warnings()
images = requests.get(startUrl, headers=headers, params=params, verify=False, timeout=4)
if images.status_code == 200:
return images.text
except requests.RequestException:
return None
def download_pic(url,name,path):
if not os.path.exists(path):
os.makedirs(path)
try:
res=urllib.request.urlopen(url,timeout=3).read()
with open(path+name+'.jpg','wb') as file:
file.write(res)
file.close()
except Exception as e:
print(str(e))
def get_headers():
headers = {
'Accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Host': 'quanjing.com',
'Referer': 'https://quanjing.com/search.aspx?',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
return headers
def get_params():
params = {'t': '3380', 'callback': 'searchresult',
'q': '籃球',
'stype': '1',
'pagesize': '100', 'pagenum': '3',
'imageType': '2', 'imageColor': '', 'brand': '', 'imageSType': '',
'fr': '1', 'sortFlag': '1', 'imageUType': '', 'btype': '',
'authid': '', '_': '1568595066763'
}
return params
def get_url():
return "https://quanjing.com/Handler/SearchUrl.ashx?"
if __name__ == '__main__':
# 使用線程池來執行線程任務的步驟如下:
#1、調用 ThreadPoolExecutor 類的構造器創建一個線程池。
#2、定義一個普通函數作爲線程任務。
#3、調用 ThreadPoolExecutor 對象的 submit() 方法來提交線程任務。
#4、當不想提交任何任務時,調用 ThreadPoolExecutor 對象的 shutdown() 方法來關閉線程池。
lists = []
list1 = (1,2,'d:/download/全景網1/','線程A') # 1:起始頁,2:結束頁 ,d:/download/全景網1/:圖片存儲路徑 線程A:線程名
list2 = (6, 7, 'd:/download/全景網1/', '線程B')
list3 = (11, 12, 'd:/download/全景網1/', '線程C')
list4 = (16, 17, 'd:/download/全景網1/', '線程D')
list5 = (21, 22, 'd:/download/全景網2/', '線程E')
lists.append(list1)
lists.append(list2)
lists.append(list3)
lists.append(list4)
lists.append(list5)
threadPool = ThreadPoolExecutor(max_workers=5)
for li in lists:
future =threadPool.submit(run,li)
threadPool.shutdown(wait=True)
print('*' * 10 + '下載完成!' + '*' * 10)
有任何疑問,均可留言或私信我交流,歡迎關注,我會持續更新博客!