都2020年了還沒爬過今日頭條,你做爬蟲是不是顯得OUT了?但是沒事,雖然現在的接口都變化了,那我就講下2020年怎麼搞今日頭條妹子寫真,這是一個改進的項目,裏面參加了我自己的很多想法,比如有些很難懂的,我自己用簡單的方式去實現了它,個人感覺還是實現的不錯的,各位看官可以看看。
這個妹子覺得好看的評論區扣好看,直接教你怎麼搞到手!
項目介紹:
利用簡單的進程池和Ajax數據爬取技術對今日頭條關鍵詞頁面進行分析和處理,然後獲取每個頁面的鏈接,從而獲得所有圖片的鏈接,然後打包下載,
整個步驟我都會用小模塊代碼將其展示,實現不了的過來砍我! 就是這麼的負責我跟你們說。
項目技術:
簡單的進程池:
這裏對進程的瞭解我也不是很多,簡單的說下項目需要的函數:
from multiprocessing import Pool # 調用函數庫
p = Pool(4) # 構造一個進程池,單位爲4
p.close() # 關閉進程
p.join() # 開啓進程
對Pool對象調用join()方法會等待所有子進程執行完畢,調用join()之前必須先調用close(),調用close()之後就不能繼續添加新的Process了。
Ajax數據爬取:
網址的很多信息都不會直接全部出現在源代碼裏面,比如你刷網頁,那些新刷出的網頁就是一個個的通過ajax接口加載出來的,這是一種異步加載方式,原始的頁面不會包含很多數據,數據都放在一個個接口裏面,只有我們請求這個ajax接口,然後服務器後臺收到這個接口信息,纔會把數據返回,然後JavaScript分析這個數據,在渲染到瀏覽器頁面上,這就是我們看到的模式,
現在越來越多的網頁都是採用這個異步加載的方式,爬蟲就現得沒那麼容易了,這個概念的講的也拗口,我們直接開始實戰吧!
項目作業:
最終作業效果圖:
分析ajax接口獲得數據:
數據包括:
每個頁面的標題
每個頁面的網址
目標網址: 今日頭條關鍵詞妹子
怎麼知道他是不是ajax接口,主要有三點:
第一點
注意我這幾個箭頭,只要你在這裏·查找·裏面找不到與文章對應的文字還是鏈接什麼的,那就可能是。
第二點
在這個XHR裏面找到箭頭的網址,點擊,查看預覽,這個時候你隨意打開裏面的東西,就能發現很多與文章相同的點
第三點
還是這張圖,你可以看到X-requested裏面的接口是XMLHttpRequets
如果三點同時滿足,那他就是Ajax接口,然後異步加載出來的數據。
獲得數據
在第二點的那張圖我們可以看到有0,1,2,3,4,之類的,打開你會發現,都在這裏面,圖中我用箭頭標紅了,有標題和頁面鏈接,只要獲得這個頁面鏈接,那麼就很簡單了。
編寫程序:
獲得json文件:
首先請求最開始的頁面: 美女_頭條搜索
但是我們不能這樣直接把頁面交給requests庫直接幹,因爲這是一個ajax接口,如果不加入參數,很可能讓你輸入什麼驗證碼還是拉動驗證條什麼,反正就是很麻煩,那我們就加入參數,具體措施如下:
def get_page(offset): # offset偏移,因爲每個ajax都是加載固定的頁面數
# 這裏是20,在第三點圖上可以看得到
global headers # 全局變量 我後面還要用
headers = {
'cookie': 'tt_webid=6821518909792273933; WEATHER_CITY=%E5%8C%97%E4%BA%AC; SLARDAR_WEB_ID=b4a776dd-f454-43c6-81cd-bd37cb5fd0ec; tt_webid=6821518909792273933; csrftoken=4a2a6afcc9de4484af87a2ff8cba0638; ttcid=8732e6def0484fae975c136222a44f4932; s_v_web_id=verify_k9o5qf2w_T0dyn2r8_X6CE_4egN_9OwH_CCxYltDKYSQj; __tasessionId=oxyt6axwv1588341559186; tt_scid=VF6tWUudJvebIzhQ.fYRgRk.JHpeP88S02weA943O6b6-7o36CstImgKj1M3tT3mab1b',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36 Edg/81.0.416.68',
'referer': 'https://www.toutiao.com/search/?keyword=%E7%BE%8E%E5%A5%B3',
'x-requested-with': 'XMLHttpRequest'
} # 頭信息 加入參數
params = {
'aid': ' 24',
'app_name': ' web_search',
'offset': offset,
'format': ' json',
'keyword': ' 美女',
'autoload': ' true',
'count': ' 20',
'en_qc': ' 1',
'cur_tab': ' 1',
'from': ' search_tab',
'pd': ' synthesis',
'timestamp': int(time.time())
}
url = 'https://www.toutiao.com/api/search/content/?' + urlencode(params) # 構造url, 使用到了urlencode() 函數
url = url.replace('=+', '=') # 這裏必須注意現在的網址根本不一樣
# print(url)
try:
r = requests.get(url, headers=headers, params=params)
r.content.decode('utf-8')
if r.status_code == 200:
return r.json() # 返回json格式 因爲全是字典類型
except requests.ConnectionError as e:
print(e)
這裏必須注意一點,請求的網址已經改掉了,我在代碼裏面給瞭解釋,仔細看看。
def get_image(json): # 獲取圖片
if json.get('data'): # 如果這個存在
for item in json.get('data'):
if item.get('title') is None:
continue # 如果標題是空值
title = item.get('title') # 獲取標題
if item.get('article_url') == None:
continue
url_page = item.get('article_url')
# print(url_page)
rr = requests.get(url_page, headers=headers)
if rr.status_code == 200:
pat = '<script>var BASE_DATA = .*?articleInfo:.*?content:(.*?)groupId.*?;</script>' # 用正則大致匹配一下範圍
match = re.search(pat, rr.text, re.S)
if match != None:
result = re.findall(r'img src=\\"(.*?)\\"', match.group(), re.S)
# print(i.encode('utf-8').decode('unicode_escape')
# 轉換編碼方式 把\u之類的改掉
yield {
'title': title,
'image': result
}
這裏獲取的網頁鏈接都是Unicode格式的,在後面的下載部分,我給了修改方案,這也是一個暗坑。
def save_image(content):
path = 'D://今日頭條美女//' # 目錄
if not os.path.exists(path): # 創建目錄
os.mkdir(path)
os.chdir(path)
else:
os.chdir(path)
# ------------------------------------------
if not os.path.exists(content['title']): # 創建單個文件夾
if '\t' in content['title']: # 以title爲標題創建單個文件夾
title = content['title'].replace('\t', '') # 去除特殊符號 不然創建不了文件名稱
os.mkdir(title + '//')
os.chdir(title + '//')
print(title)
else:
title = content['title']
os.mkdir(title + '//') # 創建文件夾
os.chdir(title + '//')
print(title)
else: # 如果存在
if '\t' in content['title']: # 以title爲標題創建單個文件夾
title = content['title'].replace('\t', '') # 去除特殊符號 不然創建不了文件名稱
os.chdir(title + '//')
print(title)
else:
title = content['title']
os.chdir(title + '//')
print(title)
for q, u in enumerate(content['image']): # 遍歷圖片地址列表
u = u.encode('utf-8').decode('unicode_escape')
# 先編碼在解碼 獲得需要的網址鏈接
# 開始下載
r = requests.get(u, headers=headers)
if r.status_code == 200:
# file_path = r'{0}/{1}.{2}'.format('美女', q, 'jpg') # 文件的名字和地址,用三目運算符來調試文件夾的名字
# hexdisgest() 返回十六進制圖片
with open(str(q) + '.jpg', 'wb') as fw:
fw.write(r.content)
print(f'該系列----->下載{q}張')
在U變量的時候進行了編碼在解碼操作,然後網址就正常很多了。
項目全部代碼:
# -*- coding '':'' utf-8 -*-''
# @Time '':'' 2020/5/1 9:34''
# @author '':'' 沙漏在下雨''
# @Software '':'' PyCharm''
# @CSDN '':'' https://me.csdn.net/qq_45906219''
import requests
from urllib.parse import urlencode # 構造url
import time
import os
from hashlib import md5
from lxml import etree
from bs4 import BeautifulSoup
import re
from multiprocessing.pool import Pool
def get_page(offset):
global headers
headers = {
'cookie': 'tt_webid=6821518909792273933; WEATHER_CITY=%E5%8C%97%E4%BA%AC; SLARDAR_WEB_ID=b4a776dd-f454-43c6-81cd-bd37cb5fd0ec; tt_webid=6821518909792273933; csrftoken=4a2a6afcc9de4484af87a2ff8cba0638; ttcid=8732e6def0484fae975c136222a44f4932; s_v_web_id=verify_k9o5qf2w_T0dyn2r8_X6CE_4egN_9OwH_CCxYltDKYSQj; __tasessionId=oxyt6axwv1588341559186; tt_scid=VF6tWUudJvebIzhQ.fYRgRk.JHpeP88S02weA943O6b6-7o36CstImgKj1M3tT3mab1b',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36 Edg/81.0.416.68',
'referer': 'https://www.toutiao.com/search/?keyword=%E7%BE%8E%E5%A5%B3',
'x-requested-with': 'XMLHttpRequest'
} # 頭信息 加入參數
params = {
'aid': ' 24',
'app_name': ' web_search',
'offset': offset,
'format': ' json',
'keyword': ' 美女',
'autoload': ' true',
'count': ' 20',
'en_qc': ' 1',
'cur_tab': ' 1',
'from': ' search_tab',
'pd': ' synthesis',
'timestamp': int(time.time())
}
url = 'https://www.toutiao.com/api/search/content/?' + urlencode(params) # 構造url
url = url.replace('=+', '=') # 網址根本不一樣
# print(url)
try:
r = requests.get(url, headers=headers, params=params)
r.content.decode('utf-8')
if r.status_code == 200:
return r.json() # 返回json格式 因爲全是字典類型
except requests.ConnectionError as e:
print(e)
def get_image(json): # 獲取圖片
if json.get('data'): # 如果這個存在
for item in json.get('data'):
if item.get('title') is None:
continue # 如果標題是空值
title = item.get('title') # 獲取標題
# if item.get('image_list') is None: # 進行判空
# continue
# urls = item.get('image_list') # 獲得圖片網址
# for url in urls: # 遍歷這個urls
# url = url.get('url')
# # 使用正則拼接網址
# url = 'http://p1.pstatp.com/origin/' + 'pgc-image/' + url.split('/')[-1]
if item.get('article_url') == None:
continue
url_page = item.get('article_url')
# print(url_page)
rr = requests.get(url_page, headers=headers)
if rr.status_code == 200:
pat = '<script>var BASE_DATA = .*?articleInfo:.*?content:(.*?)groupId.*?;</script>'
match = re.search(pat, rr.text, re.S)
if match != None:
result = re.findall(r'img src=\\"(.*?)\\"', match.group(), re.S)
# for i in result:
# print(i.encode('utf-8').decode('unicode_escape')
# 轉換編碼方式 把\u之類的改掉
yield {
'title': title,
'image': result
}
# 格式出錯,這裏產生了十六進制的數值, 網址獲取不了,明天看
# yield {
# 'title': title,
# 'image': url
# } # 返回標題和網址
def save_image(content):
path = 'D://今日頭條美女//' # 目錄
if not os.path.exists(path): # 創建目錄
os.mkdir(path)
os.chdir(path)
else:
os.chdir(path)
# ------------------------------------------
if not os.path.exists(content['title']): # 創建單個文件夾
if '\t' in content['title']: # 以title爲標題創建單個文件夾
title = content['title'].replace('\t', '') # 去除特殊符號 不然創建不了文件名稱
os.mkdir(title + '//')
os.chdir(title + '//')
print(title)
else:
title = content['title']
os.mkdir(title + '//') # 創建文件夾
os.chdir(title + '//')
print(title)
else: # 如果存在
if '\t' in content['title']: # 以title爲標題創建單個文件夾
title = content['title'].replace('\t', '') # 去除特殊符號 不然創建不了文件名稱
os.chdir(title + '//')
print(title)
else:
title = content['title']
os.chdir(title + '//')
print(title)
for q, u in enumerate(content['image']): # 遍歷圖片地址列表
u = u.encode('utf-8').decode('unicode_escape')
# 先編碼在解碼 獲得需要的網址鏈接
# 開始下載
r = requests.get(u, headers=headers)
if r.status_code == 200:
# file_path = r'{0}/{1}.{2}'.format('美女', q, 'jpg') # 文件的名字和地址,用三目運算符來調試文件夾的名字
# hexdisgest() 返回十六進制圖片
with open(str(q) + '.jpg', 'wb') as fw:
fw.write(r.content)
print(f'該系列----->下載{q}張')
def main(offset):
json = get_page(offset)
get_image(json)
for content in get_image(json):
try:
# print(content)
save_image(content)
except FileExistsError and OSError:
print('創建文件格式錯誤,包含特殊字符串:')
continue
if __name__ == '__main__':
pool = Pool()
groups = [j * 20 for j in range(8)]
pool.map(main, groups) # 傳offset偏移量
pool.close()
pool.join()
項目修復:
正確的拼接網址,修復了data = None 的bug,在獲得json文件裏面得到了體現
加入必要的參數,避免了驗證碼和驗證條的出現,這也很棒 =。=
進行編碼解碼操作, 修改了\u格式下的鏈接不匹配問題,在獲得網址的時候我也提到了
下載圖片的時候使用了子文件夾的處理,讓下載的圖片不會那麼雜亂
下載採用了簡單的線程池問題,加快了下載的速度
討論交流:313074041