回敬加餐:我是怎樣獲取視頻中的AO3文章的

寫在前面:

就在昨天,豆醬的某乎在第二次15天禁言之後,

又被有計劃的禁言了15天,也就是說截止昨天(2020/3/27),豆醬因為之前的評論已經被禁言過7+15天,並將再加未來15天。可見這個並不是一時衝動的行為,而是有計劃的。首先我相信某乎的公正性,也感謝做這個事情的人沒對我的號做什麼。 但是作為對豆醬昨天被禁言的回應,今天我的文章會調整一下。原計劃是給大家科普自然語言處理(NLP)和文本分類,但今天我會先放出我B站視頻展示的約600篇文章是如何爬取的技術文。

最後我想對那些不喜歡我的人說:我是個技術人,我開始嘗試做內容是希望讓大家展示好玩的技術,吸引大家學習知識。雖然我寫的文章和視頻引起了爭議,但是我一直希望跟大家講解的如何獲得和爬取數據,如何分析,我的結論是如何獲得的,希望大家喜歡這樣探討問題的方法。我雖然有自己的觀點,我夫人也喜歡肖戰,但我們並不想針對或死磕任何人。 你們正在使用你們所不齒的方法針對我們。 古話有云:己所不欲勿施於人。

文章無圖有料,不懂技術的各位也應該仔細瞧瞧。


在之前的文章中,我已經和大家分享瞭如何直接爬取AO3的文章,那麼如何找到文章的關聯關係是一件比較頭疼的問題。如果自己去寫爬蟲會比較浪費資源。最省事的方法就是藉助搜索引擎進行資料獲取。這裡,我們就以 lofter 到 AO3 的外鏈為例。

加載的函數庫還是與之前相同,這裡我不再複述。

<code>import sys
import re
import os
import time

from tqdm import tqdm

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from bs4 import BeautifulSoup

import random/<code>

這裡和大家科普一下搜索引擎的高級搜索模式,平時大家使用搜索引擎,可能都是一整句話放進去搜索。但實際上搜索引擎是支持一定的高級語法以方便獲取到更高級的內容。我們以谷歌為例:

“”精確匹配

使用引號來搜索一個完全匹配的字詞或一組字詞。在搜索歌詞或文學作品中的一段文字時,此選項很實用。建議您只在查找非常確切的字詞或詞組時使用該功能,否則可能會無意中排除掉有用的搜索結果。

例如“見與不見” 搜索結果精確匹配“見與不見”,不能拆分成“見”與“不見”。

-排除字詞

在某個字詞前添加短橫 (-) 可排除所有包含該字詞的搜索結果。

例如:大熊貓 -百科 搜索結果中不出現“百科”

OR選擇性字詞搜索

選擇性字詞OR搜索結果匹配多個搜索字詞中的任意一個。沒有OR搜索結果中通常只會顯示與多個字詞都匹配的網頁。

例如:奧運會 2014 OR 2018 搜索結果中會出現 “奧運會 2014”或者“奧運會 2018”的結果

site在特定網站或域名中搜索

在搜索中加入“site:”可以限定在某個特定網站中搜索信息

例如:LOFTER site:lofter.com

“site:”後面跟的站點域名,不要帶“http://”。site:和站點名之間,不要帶空格。

inurl在特定url鏈接中搜索

在搜索中加入“inurl:”可以限定在網站url鏈接中搜索網站信息

例如:auto視頻教程 inurl:video

搜索詞“auto視頻教程”是可以出現在網頁的任何位置,而“video”則必須出現在網頁url中。

以上只是谷歌的部分高級搜索語法,百度也有類似的使用方法,大家可以自己去查查詳細的使用方法。我們這裡用到了 site: 標籤 和 inurl: 標籤 也就是:

site:lofter.com inurl:ao3

這條語句的含義是,在 lofter.com 中 搜索 含有 ao3 鏈接 的結果。這裡需要注意,實際搜索過程中,"ao3" 需要換成該網站的實際域名。這裡因為不想透露真實網站地址所以使用了 "ao3" 替代。

分析url 的思路我在 《我是怎樣得到AO3內容的》 有介紹過,這裡直接給結論。谷歌的url 由 search?後的內容構成:

  • hl=en 表示搜索語言為英文
  • q= 後跟搜索內容
  • safe= 跟的是是否為安全搜索,這裡使用images參數關閉安全搜索也就是可以搜索到不好的信息~
  • num= 表示每頁展示的搜索條數
  • start= 表示從第幾條開始顯示,所以翻頁的計算方法為 start = page*num

這裡說明一下,我確實專門搜索了語言為英文的頁面,但搜索引擎的模糊性使得結果依然有大部分是中文文章。但是我可以證明兩點:

  1. 之前有說在ao3 看英文或學英語是真實的;
  2. 我還沒有開始做文本分析,但就我看過的幾篇英文文章中,以我留過學的經歷來衡量,文章中確實含有書本上一般學不到的東西和詞彙;【手動狗頭】

言歸正傳看代碼:

<code>#獲谷歌取搜索頁面
def make_google_search_url(page=0, num=100):
base_loc = 'https://www.google.com/search?hl=en&q=site:lofter.com+inurl:ao3&safe=images'
base_loc += "&num="+str(num)
base_loc += "&start="+str(page*num) #搜索頁

return base_loc/<code>

獲取鏈接的方法依然是 chrome 瀏覽器調試模式(F12)分析元素並用 BeautifulSoup 解析,這裡不再複述,大家直接看代碼。

<code>#從谷歌獲取文章鏈接 

def get_url_from_search(html):
old_list = []
soup = BeautifulSoup(html, 'html.parser')
search_div = soup.find('div', attrs={'id': 'search'})
div_g_groups = search_div.findAll('div', attrs={'class': 'g'})
for g in div_g_groups:
div_r = g.find('div', attrs={'class': 'r'})
a_hurl = div_r.find('a')
old_list.append(a_hurl['href'])

return old_list/<code>

最後就是判斷 lofter 的頁面中是否含有 有效的 ao3 鏈接。按照之前的經驗,判定含有 works 的 url 才考慮為有外鏈文章。但是在後來實踐過程中 發現含有 users 的外鏈也非常有意思,就一併保存了。

保存的內容有: lofter 頁面,本 lofter 頁面中所有含有 ao3 外鏈的鏈接,所有涉及的 ao3 原文頁面,ao3 用戶介紹頁(內含該用戶所有文章)

注意,目前目前我只是保存了 ao3 用戶介紹頁(如果有)。並沒有進行二次爬取或分析。

另外相比 《我是怎樣得到AO3內容的》中的函數,這裡進行了優化,當出現“Retry later”時,函數會自動重試,而不會想之前就直接把這一頁放過不保存了。

代碼中 ao3 站點地址我使用 xxx 代替。

<code>def find_ao3_from_lofter(lofter_url_list, browser, path):
for url in lofter_url_list:
print(url)
dir_name = (
url.replace("http://", "")

.replace(".com/", "_")
.replace("/", "_")
.replace(".", "_")
)
dir_path = os.path.join(path, dir_name)
isExists = os.path.exists(dir_path)
if isExists:
print("Exists")
continue
# 判斷結果
ao3_links = []
browser.get(url)
currurl = browser.current_url
if "xxx" in currurl and (
"/works/" in currurl or "/users/" in currurl
): # 如果url 直接跳轉
ao3_links.append(currurl)
lhtml = ""
else: # 沒有跳轉
lhtml = browser.page_source
soup = BeautifulSoup(lhtml, "html.parser")
alink_groups = soup.findAll("a", attrs={"rel": "nofollow"})
for alink in alink_groups:
href_str = alink["href"]
if "xxx" in href_str and (
"/works/" in href_str or "/users/" in href_str
):
ao3_links.append(href_str)

if ao3_links:
# 判斷路徑是否存在
isExists = os.path.exists(dir_path)

# 如果不存在則創建目錄
os.makedirs(dir_path)

links_str = url + "\\n"

need_agree = True
for work_url in ao3_links: # 遍歷ao3鏈接
links_str += work_url + "\\n"

print(os.path.join(dir_path, "links.txt"))
fh = open(os.path.join(dir_path, "links.txt"), "w") # 保存頁面
fh.write(links_str) # 寫入內容
fh.close() # 關閉


print(os.path.join(dir_path, "lofter.html"))
fh = open(os.path.join(dir_path, "lofter.html"), "w") # 保存頁面
fh.write(lhtml) # 寫入內容
fh.close() # 關閉
for work_url in ao3_links:
browser.get(work_url)

if need_agree:
try:
time.sleep(3)
browser.find_element_by_id("tos_agree").click()
time.sleep(1)
browser.find_element_by_id("accept_tos").click()
time.sleep(1)

need_agree = False
except NoSuchElementException:
need_agree = False

work_html_text = browser.page_source # 獲得頁面代碼

work_name = (
work_url.replace("https://", "")
.replace("http://", "")
.replace("xxx", "")
.replace(".com/", "")
.replace(".org/", "")
.replace("/", "_")
.replace(".", "_")
.replace("#", "_")
)
work_path = os.path.join(dir_path, work_name + ".html")

if (
'If you accept cookies from our site and you choose "Proceed"'
in work_html_text
): # 無法獲取正文則點擊Proceed
browser.find_element_by_link_text("Proceed").click()
time.sleep(1)
browser.get(work_url)
work_html_text = browser.page_source
if "Retry later" in work_html_text:
while "Retry later" in work_html_text:
print(work_path)
fh = open(work_path, "w") # 保存頁面
fh.write("Need_to_reload") # 寫入內容

fh.close() # 關閉
print("Retry Later")
time.sleep(3)
browser.get("http://www.baidu.com")
time.sleep(3)
browser.quit()
c_service.stop()
time.sleep(60)
c_service.start()
browser = webdriver.Chrome(
chrome_options=chrome_options
) # 調用Chrome瀏覽器
browser.get("https://xxx.org/")
time.sleep(5)
browser.find_element_by_id("tos_agree").click()
time.sleep(2)
browser.find_element_by_id("accept_tos").click()
time.sleep(3)

browser.get(work_url)

work_html_text = browser.page_source # 獲得頁面代碼

if (
'If you accept cookies from our site and you choose "Proceed"'
in work_html_text
): # 無法獲取正文則點擊Proceed
browser.find_element_by_link_text("Proceed").click()
time.sleep(1)
browser.get(work_url)
work_html_text = browser.page_source

# if "" in work_html_text:
print(work_path)
fh = open(work_path, "w") # 保存頁面
fh.write(work_html_text) # 寫入內容
fh.close() # 關閉
time.sleep(float(random.randint(10, 50)) / 10) # 隨機延時
return browser/<code>

設置起止頁

<code>start_p = 0
end_p = 4/<code>

如果平凡使用谷歌,谷歌會啟動防機器人機制,這是函數會暫停等待我人工解鎖的。

所以這裡我也相當於解釋了我為什麼沒有翻牆,因為如果我使用翻牆軟件爬取,是會被谷歌發現並封殺掉的,而如何繞過呢?賣個關子,看看有沒有懂行的朋友幫大家解釋一下。

<code>c_service = webdriver.chrome.service.Service("/usr/bin/chromedriver")
c_service.command_line_args()
c_service.start()

chrome_options = webdriver.ChromeOptions()
# chrome_options.add_argument('--proxy-server=socks5://localhost:1080')

auto_quit_cnt = 0
browser = webdriver.Chrome(chrome_options=chrome_options) # 調用Chrome瀏覽器
for page in range(start_p, end_p):
print("-" * 30)
print("Page: " + str(page))
print("-" * 30)
google_search_url = make_google_search_url(page)
browser.get(google_search_url)

html_text = browser.page_source # 獲得頁面代碼
while "Our systems have detected unusual traffic" in html_text:
print("Google Robot!")
time.sleep(10)
html_text = browser.page_source # 獲得頁面代碼
auto_quit_cnt += 1
if auto_quit_cnt > 30:
break
auto_quit_cnt = 0
lofter_list = get_url_from_search(html_text)
browser = find_ao3_from_lofter(lofter_list, browser, "lofter")
/<code>

寫在最後:


關於AO3這個系列,我還剩最後兩篇文章:

  • 基於深度學習的 NLP 文本分類器;
  • 基於OpenCV 的圖像視頻製作.
  • 這個話題做了快一個月了,我希望能夠將我想講的技術安安靜靜講完。然後再帶著大家探索其他有意思的編程技術,而不是揪著這個話題不放。

    所以再次申明,我只是分析 AO3 其他事情我不做探討和引申,也懇請大家理性思考和探討。上文中我已經有限擴大了討論範圍。我的下一篇文章會按照我的規劃來,我的下一個視頻會是另一個好玩的技術。


    我也希望即使你不喜歡我,也不要討厭技術,不要討厭學習。

    在這段時間之前我是沒有做Python數據分析的相關知識的;雖然同屬深度學習,NLP不是我的專業,所以我也是第一次實踐,但是通過這個熱點,我收穫了很多新知識,也有很多人給我點贊鼓勵交流探討。我收穫了很多。

    但是,

    你收穫了什麼呢?


    分享到:


    相關文章: