閒得流口水之爬取頭條MM(MM勿進)

都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&#x3D;\\&quot;(.*?)\\&quot;', 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&#x3D;\\&quot;(.*?)\\&quot;', 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章