Python:錄記個做,寫寫便隨

這篇博文介紹的內容包括:

  1. 網絡爬蟲中 selenium 的使用
  2. 異步請求後臺與服務器的交互文件
  3. 實現在固定時間重複執行特定代碼塊

當然,有了這些技能可以完成什麼任務呢?看完這篇博文的讀者,一定會有意想不到的大收穫,哈哈!!!

注:建議先看評論再看內容!!!

注:建議先看評論再看內容!!!


目錄

一、工具

(一)selenium安裝

(二)瀏覽器驅動

 (三)其他工具

 二、思路

(一)登錄

(二)獲取博客文章id

(三)一個小小的打斷

(四)隨機訪問文章

三、全部代碼

(一)代碼

 (二)輸出


一、工具

工欲善其事必先利其器”,首先我們看看需要哪些“工具”把。

(一)selenium安裝

selenium 是一個用於Web應用程序測試的工具selenium 測試直接運行在瀏覽器中,就像真正的用戶在操作一樣。支持的瀏覽器包括IE(7, 8, 9, 10, 11)Mozilla FirefoxSafariGoogle ChromeOpera 等。這個工具的主要功能包括:測試與瀏覽器的兼容性——測試你的應用程序看是否能夠很好得工作在不同瀏覽器和操作系統之上。測試系統功能——創建迴歸測試檢驗軟件功能和用戶需求。支持自動錄製動作和自動生成 . NetJavaPerl 等不同語言的測試腳本。[1]

所以我們拿 selenium 來模擬用戶的真實操作,就可以躲過網站的反爬啦!

selenium 可以直接可以用 pip 安裝:

pip install selenium

(二)瀏覽器驅動

作者使用的是谷歌瀏覽器,所以這裏就只介紹 chromedriver 了。

1. 首先需要 Chrome 版本,在 Chrome 瀏覽器中輸入 chrome://version/,得到下圖1

圖1:chrome版本

可以看到我的版本爲:80.0.3987.106 (64位),操作系統爲:Linux,所以下載的 chromedriver 也要相互匹配。

2. chromedriver 下載的地址是:http://chromedriver.storage.googleapis.com/index.html,找到並選擇與自己版本匹配的即可,如下圖2、3

圖2:80.0.3987.106
圖3:chromedriver_linux64.zip202

 3. 把壓縮包解壓,解壓後的 chromedriver.exe 保存到你的本地文件夾即可。

 (三)其他工具

其他工具主要就是 python 的模塊包啦。主要有:

from threading import Timer
import json

沒有的話,讀者就自行 pip 把。

 二、思路

我們的整體思路就是通過使用 selenium 來模擬用戶點擊瀏覽每一篇 blog 來 increase traffic,具體步驟如下:

(一)登錄

作者這裏爲了方便,登錄過程沒有寫特別多的代碼,方法就是: selenium 打開登錄界面——>用戶拿起手機微信掃碼登錄(哈哈,沒想到吧,但是這個方法簡單有效),然後其他工作就全部交給代碼自己運行啦,讀者就可以坐等瀏覽量增加(或者代碼崩掉)。

相關代碼如下:

# 獲取chromedriver
def get_driver(executable_path, show=0):
    # 創建參數設置對象
    chrome_opt = Options()

    # show參數決定是否顯示瀏覽器,因爲顯示瀏覽器界面需要消耗計算機內存,所以要根據情況來定
    # 0表示不顯示,1表示顯示
    if show == 0:
        # 無界面化
        chrome_opt.add_argument('--headless')
        # 配合上面的無界面化
        chrome_opt.add_argument('--disable-gpu')

        # 不加載圖片
        prefs = {"profile.managed_default_content_settings.images": 2}
        chrome_opt.add_experimental_option("prefs", prefs)
    elif show == 1:
        pass

    # 設置窗口大小, 窗口大小會有影響
    chrome_opt.add_argument('--window-size=1366,768')
    # 使用沙盒模式運行
    chrome_opt.add_argument("--no-sandbox")

    # 創建Chrome對象並傳入設置信息
    driver = webdriver.Chrome(executable_path=executable_path, chrome_options=chrome_opt)
    return driver


# chromedriver的可執行路徑
executable_path = '/usr/local/bin/chromedriver/chromedriver'

# 登錄url
url = 'https://passport.csdn.net/login'

# 獲得driver
# 顯示界面
driver = get_driver(executable_path, 1)

# 請求登錄界面
driver.get(url)
# 這裏等待15s是爲了用戶完成掃碼登錄過程
sleep(15)

讀者要提前準備好手機微信,如果手速慢的可以把時間調大一點。

注:首次登錄需要手機號驗證,所以建議首次運行代碼將時間調大很多。 

效果如下圖4

圖4:登錄

(二)獲取博客文章id

我們可以看看本篇博客的 url ,大概格式就是 https://blog.xxxx.net/qq_用戶id/article/details/文章id ,所以爲了遍歷所有文章,那就得先獲取所有用戶 id(用戶 id 其實就是你的 id 啦,但是我不清楚轉載的文章指向是否也是自己的 id ,所以這裏統一記錄下來了)和文章 id

這裏需要注意一點的是,作者在獲得博客文章 id 時,找到了一個捷徑,就是通過請求如下連接:https://blog-console-api.csdn.net/v1/article/list?page=1&pageSize=20,可以獲得一個 json 文件,裏面記錄了博客文章的所有信息(如果讀者想知道作者是如何找到的,可以看我的另一篇博客:https://blog.xxxx.net/qq_41297934/article/details/104463851,這篇文章會給你思路哦)。

所以我們只需要請求該 api 接口,就可以獲取用戶 id文章 id 了,代碼如下:

# 請求博客列表
# page=1表示第一頁的博客
driver.get('https://blog-console-api.csdn.net/v1/article/list?page=1&pageSize=20')
sleep(10)
html = driver.page_source

# 通過正則表達式來匹配json內容
html_str = re.findall(r'>(\{.+?})<', html.replace('\n', '').replace(' ', ''))[0]

# 獲取博客id列表和用戶名列表
article_id_list = []
username_list = []
html_dict = json.loads(html_str)
for j in html_dict['data']['list']:
    article_id_list.append(j['ArticleId'])
    username_list.append(j['UserName'])

請求到的 json 文件會在瀏覽器中顯示一次的,效果如下圖5 : 

圖5:json文件

(三)一個小小的打斷

按理說我們就可以用  selenium 模擬用戶來訪問文章啦,但是作者親身經歷發現,即使代碼沒有問題的情況下,每次打開網頁的時間實在太長,所以決定寫一個在規定時間內強制執行程序的代碼,這樣只要網頁被打開,不等其加載完成就將其關閉。

這裏參考了別人的代碼[2],該文作者的解釋如下:

今天小編就爲大家分享一篇Python 實現某個功能每隔一段時間被執行一次的功能方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧。

某個函數需要在每個小時的 3 分鐘時候被執行一次,我希望我 15:45 啓動程序,過了18 分鐘在 16:03 這個函數被執行一次,下一次過 60 分鐘在 17:03 再次被執行,下一次 18:03,以此類推。

以下是我基於 Timer 做的再封裝實現了此功能。

代碼如下:

# -*- coding: utf-8 -*-
# ==================================================
# 對 Timer 做以下再封裝的目的是:當某個功能需要每隔一段時間被
# 執行一次的時候,不需要在回調函數裏對 Timer 做重新安裝啓動
# ==================================================
__author__ = 'liujiaxing'

from threading import Timer
from datetime import datetime


class MyTimer(object):

    def __init__(self, start_time, interval, callback_proc, args=None, kwargs=None):
        self.__timer = None
        self.__start_time = start_time
        self.__interval = interval
        self.__callback_pro = callback_proc
        self.__args = args if args is not None else []
        self.__kwargs = kwargs if kwargs is not None else {}

    def exec_callback(self, args=None, kwargs=None):
        self.__callback_pro(*self.__args, **self.__kwargs)
        self.__timer = Timer(self.__interval, self.exec_callback)
        self.__timer.start()

    def start(self):
        interval = self.__interval - (datetime.now().timestamp() - self.__start_time.timestamp())
        print(interval)
        self.__timer = Timer(interval, self.exec_callback)
        self.__timer.start()

    def cancel(self):
        self.__timer.cancel()
        self.__timer = None


class AA:
    @staticmethod
    def hello(name, age):
        print("[%s]\thello %s: %d\n" % (datetime.now().strftime("%Y%m%d %H:%M:%S"), name, age))


if __name__ == "__main__":
    aa = AA()
    start = datetime.now().replace(minute=3, second=0, microsecond=0)
    tmr = MyTimer(start, 60 * 60, aa.hello, ["owenliu", 18])
    tmr.start()
    tmr.cancel()

我們需要注意的代碼塊爲:

tmr = MyTimer( start, 60*60, aa.hello, [ "owenliu", 18 ] )
def hello(name, age):
        print("[%s]\thello %s: %d\n" % (datetime.now().strftime("%Y%m%d %H:%M:%S"), name, age))

 hello(name, age) 爲回調函數,tmr = MyTimer(start, 60 * 60, aa.hello, ["owenliu", 18]) 中,傳遞了 4 個參數,start 爲當前時間,60*60 爲代碼循環週期(這裏爲 1h ),aa.hello 爲回調函數,["owenliu", 18] 爲回調函數的參數。讀者可以將時間改爲 10 ,感受一下該代碼的奇妙之處。

(四)隨機訪問文章

使用上面的代碼,修改回調函數,就可以實現我們的功能了。

這部分代碼容易理解,就直接給出代碼了:

  • 回調函數:circle(executable_path, username_list, article_id_list)
def circle(executable_path, username_list, article_id_list):
    # 時間
    print("[%s]" % (datetime.now().strftime("%Y%m%d %H:%M:%S")))

    # 開始時間
    start = time.time()

    # 獲得driver
    # 不顯示界面
    driver = get_driver(executable_path, 0)

    # 隨機訪問
    num = randint(0, len(username_list)-1)
    # sleep(randint(3, 8))
    article_url = 'https://blog.csdn.net/' \
                  + username_list[num] \
                  + '/article/details/' \
                  + article_id_list[num]
    print('訪問網頁:%s' % article_url)
    driver.get(article_url)

    # 結束時間
    end = time.time()
    # 耗時
    print('訪問時間:%s' % (end-start))
    print('*****' * 20)

    # 關閉驅動
    driver.close()
  •  循環代碼
# 每隔10s執行一次代碼
my_start = datetime.now().replace(minute=3, second=0, microsecond=0)
my_timer = MyTimer(my_start, 10, circle, [my_executable_path, my_username_list, my_article_id_list])
my_timer.start()
my_timer.cancel()

三、全部代碼

(一)代碼

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import re
from random import randint
from time import sleep
import json
import time

from threading import Timer
from datetime import datetime


class MyTimer(object):

    def __init__(self, start_time, interval, callback_proc, args=None, kwargs=None):
        self.__timer = None
        self.__start_time = start_time
        self.__interval = interval
        self.__callback_pro = callback_proc
        self.__args = args if args is not None else []
        self.__kwargs = kwargs if kwargs is not None else {}

    def exec_callback(self):
        self.__callback_pro(*self.__args, **self.__kwargs)
        self.__timer = Timer(self.__interval, self.exec_callback)
        self.__timer.start()

    def start(self):
        interval = self.__interval - (datetime.now().timestamp() - self.__start_time.timestamp())
        self.__timer = Timer(interval, self.exec_callback)
        self.__timer.start()

    def cancel(self):
        self.__timer.cancel()
        self.__timer = None


# 獲取chromedriver
def get_driver(executable_path, show=0):
    # 創建參數設置對象
    chrome_opt = Options()

    # show參數決定是否顯示瀏覽器,因爲顯示瀏覽器界面需要消耗計算機內存,所以要根據情況來定
    # 0表示不顯示,1表示顯示
    if show == 0:
        # 無界面化
        chrome_opt.add_argument('--headless')
        # 配合上面的無界面化
        chrome_opt.add_argument('--disable-gpu')

        # 不加載圖片
        prefs = {"profile.managed_default_content_settings.images": 2}
        chrome_opt.add_experimental_option("prefs", prefs)
    elif show == 1:
        pass

    # 設置窗口大小, 窗口大小會有影響
    chrome_opt.add_argument('--window-size=1366,768')
    # 使用沙盒模式運行
    chrome_opt.add_argument("--no-sandbox")

    # 創建Chrome對象並傳入設置信息
    driver = webdriver.Chrome(executable_path=executable_path, chrome_options=chrome_opt)
    return driver


def get_username_article(executable_path, url):
    # 獲得driver
    # 顯示界面
    driver = get_driver(executable_path, 1)

    # 請求登錄界面
    driver.get(url)
    # 這裏等待15s是爲了用戶完成掃碼登錄過程
    sleep(15)

    # 請求博客列表
    # page=1表示第一頁的博客
    driver.get('https://blog-console-api.csdn.net/v1/article/list?page=1&pageSize=20')
    sleep(10)
    html = driver.page_source

    # 通過正則表達式來匹配json內容
    html_str = re.findall(r'>(\{.+?})<', html.replace('\n', '').replace(' ', ''))[0]

    # 獲取博客id列表和用戶名列表
    article_id_list = []
    username_list = []
    html_dict = json.loads(html_str)
    for j in html_dict['data']['list']:
        article_id_list.append(j['ArticleId'])
        username_list.append(j['UserName'])

    # 關閉驅動
    driver.close()

    return username_list, article_id_list


def circle(executable_path, username_list, article_id_list):
    # 時間
    print("[%s]" % (datetime.now().strftime("%Y%m%d %H:%M:%S")))

    # 開始時間
    start = time.time()

    # 獲得driver
    # 不顯示界面
    driver = get_driver(executable_path, 0)

    # 隨機訪問
    num = randint(0, len(username_list)-1)
    # sleep(randint(3, 8))
    article_url = 'https://blog.csdn.net/' \
                  + username_list[num] \
                  + '/article/details/' \
                  + article_id_list[num]
    print('訪問網頁:%s' % article_url)
    driver.get(article_url)

    # 結束時間
    end = time.time()
    # 耗時
    print('訪問時間:%s' % (end-start))
    print('*****' * 20)

    # 關閉驅動
    driver.close()


if __name__ == '__main__':
    # chrome驅動的可執行路徑
    my_executable_path = '/usr/local/bin/chromedriver/chromedriver'

    # 登錄url
    my_url = 'https://passport.csdn.net/login'

    # 獲取用戶名列表、文章id列表
    my_username_list, my_article_id_list = get_username_article(my_executable_path, my_url)

    # 每隔10s執行一次代碼
    my_start = datetime.now().replace(minute=3, second=0, microsecond=0)
    my_timer = MyTimer(my_start, 10, circle, [my_executable_path, my_username_list, my_article_id_list])
    my_timer.start()
    my_timer.cancel()

 (二)輸出

這裏給出幾次訪問文章的輸出結果,速度還是挺快的。

[20200406 18:18:32]
訪問網頁:https://blog.csdn.net/qq_41297934/article/details/105104684
訪問時間:8.050034999847412
****************************************************************************************************
[20200406 18:18:50]
訪問網頁:https://blog.csdn.net/qq_41297934/article/details/105302006
訪問時間:3.8321597576141357
****************************************************************************************************
[20200406 18:19:04]
訪問網頁:https://blog.csdn.net/qq_41297934/article/details/104551659
訪問時間:3.9448931217193604

[1] https://baike.baidu.com/item/Selenium/18266?fr=aladdin

[2] https://www.jb51.net/article/148815.htm

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