考慮到現在大部分小伙伴使用 Python 主要因為爬蟲(chóng),那么為了更好地幫助大家鞏固爬蟲(chóng)知識,加深對爬蟲(chóng)的理解,選擇了爬取百度文庫作為我們的目標。
TXT、DOCX 爬取與保存
這是小編準備的python基礎學(xué)習資料,關(guān)注,轉發(fā),私信小編“01”即可免費領(lǐng)??!
在爬取任何東西之前,我們都要先確認需要爬取的數據是不是異步加載的;如果是異步加載的直接爬取網(wǎng)頁(yè)是爬不到的。
要知道是不是異步加載其實(shí)很簡(jiǎn)單,就用requests 對網(wǎng)頁(yè)發(fā)起請求,看看 response 是什么就可以了
url = 'https://wenku.baidu.com/view/4e29e5a730126edb6f1aff00bed5b9f3f90f72e7.html?rec_flag=default'header = {'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'}res = requests.get(url , headers = header)res.text很明顯,返回的東西,并不是我們所需要的內容; 根據常理來(lái)說(shuō),我們就可以認為該網(wǎng)頁(yè)是異步加載的。
但是,從常識來(lái)講,如果網(wǎng)頁(yè)的內容是異步加載的,那么直接通過(guò)百度搜索,是搜索不到網(wǎng)頁(yè)內部的內容的,但是很顯然,我們每次通過(guò)百度搜索都是可以直接找到文庫中的文本內容的。
如下:
那么這就有意思了,明明直接發(fā)起請求是獲取不到網(wǎng)頁(yè)內容的,但是為什么通過(guò)百度搜索就可以找到呢? 關(guān)鍵肯定在于百度搜索 上面。
這個(gè)時(shí)候通過(guò)查閱資料,我們了解到,最主要的問(wèn)題出在我們的 headers
在爬取網(wǎng)頁(yè)時(shí),headers 通常是作為身份證,讓網(wǎng)頁(yè)不看出我們是爬蟲(chóng);如果不加 headers,網(wǎng)頁(yè)直接就會(huì )看出我們是爬蟲(chóng),就會(huì )拒絕訪(fǎng)問(wèn)
再深入了解一下 headers 的識別機理,我們發(fā)現了叫做 Robot 協(xié)議的東西。
它規定了什么樣的 headers 可以訪(fǎng)問(wèn)網(wǎng)頁(yè)內部?jì)热?,除了指?headers 之外的 headers,都是無(wú)法請求頁(yè)面內容的。
比如說(shuō)百度文庫的 Robot 協(xié)議就是下面這樣的:
User-agent: BaiduspiderDisallow: /w?Disallow: /search?Disallow: /submitDisallow: /uploadDisallow: /cashier/ 而我們需要爬取的內容 url 格式為
這代表 Baiduspider 應該可以爬取文庫內容;大致猜測這是因為百度搜索時(shí)需要根據文本內容匹配搜索選項,所以放行。
因此我們嘗試偽裝 UA 為 Baiduspider
url = 'https://wenku.baidu.com/view/4e29e5a730126edb6f1aff00bed5b9f3f90f72e7.html?rec_flag=default'header = {'User-agent': 'Googlebot'}res = requests.get(url , headers = header)res.text果然不出所料,我們成功地獲取到了目標內容

既然已經(jīng)成功獲取到了網(wǎng)頁(yè)的正確源代碼,那么下一步就是去解析網(wǎng)頁(yè)獲取內容。
解析網(wǎng)頁(yè)源代碼的庫有很多,這里我們使用 BeautifulSoup
plist = []soup = BeautifulSoup(r, 'html.parser')plist.append(soup.title.string)for div in soup.find_all('div', attrs={'class': 'bd doc-reader'}): plist.extend(div.get_text().split('\n'))plist = [c.replace(' ', '') for c in plist]plist = [c.replace('\x0c', '') for c in plist]plist整個(gè)解析是非常容易的,都是很標準的操作。
在這里就不多加敘述了,最終的效果如下:

當然爬取到東西了只是萬(wàn)里長(cháng)征的第一步,就這樣是肯定不行的,我們還需要將爬取的內容保存起來(lái),通常是保存為 txt 文件
file = open('test.txt', 'w',encoding='utf-8')for str in plist: file.write(str) file.write('\n')file.close()
但是為了美觀(guān)起見(jiàn),我們在這里選擇使用 python-docx 庫將內容保存為 docx 文件
with open('test.txt', encoding='utf-8') as f: docu = Document() docu.add_paragraph(f.read()) docu.save('test.docx')
PPT、PDF 爬取與保存

有了之前的經(jīng)驗教訓,在爬取的時(shí)候我們首先就嘗試了使用爬取 TXT、DOCX 的方法,嘗試是否可以爬到內容
url = 'https://wenku.baidu.com/view/a4ac1b57dd88d0d232d46a0f.html?fr=search'header = {'User-agent': 'Googlebot'}res = requests.get(url , headers = header)res.text很可惜的是,我們并沒(méi)有訪(fǎng)問(wèn)到
原因仔細想想也很簡(jiǎn)單,在百度搜索的時(shí)候,直接搜索是搜不到 PPT 或者 PDF 的內容的

那么很顯然,PPT 和 PDF 是通過(guò)異步的方法進(jìn)行內容加載的
對待異步加載的數據,我們通常采取的策略有兩種
1、第一個(gè)就是直接找到發(fā)起異步請求的接口,自己構造請求頭,發(fā)起請求
2、第二個(gè)就是通過(guò) Selenium 這樣的自動(dòng)化測試工具去爬取

百度文庫的接口太難找了,請求頭的構造也很麻煩,找了很久也沒(méi)有很滿(mǎn)意。
所以在本次爬取中,我們使用的是第二種方法,使用 Selenium 這樣的自動(dòng)化測試工具

這里我們需要下載 ChromeDriver 這個(gè)插件,當然這里是默認大家使用的是 Chrome 瀏覽器,如果是其他的瀏覽器,firefox,safari 等等,直接去網(wǎng)上找到相應 Driver 就可以了。
這里給出 ChromeDriver 的下載地址
http://npm.taobao.org/mirrors/chromedriver/
大家一定要下載和自己 Chrome 瀏覽器版本一致的 ChromeDriver,不然程序是運行會(huì )報錯

我們先不急著(zhù)馬上開(kāi)始爬取,我們先來(lái)嘗試使用一下Selenium調用ChromeDriver
import requestsfrom selenium import webdriverurl = 'https://wenku.baidu.com/view/5292b2bc0166f5335a8102d276a20029bd64638c.html?fr=search'driver = webdriver.Chrome(r'F:\driver\chromedriver.exe')driver.get(url)怎么樣,是不是瀏覽器自動(dòng)打開(kāi)了? 現在我們嘗試輸出這個(gè) driver,就可以看見(jiàn),網(wǎng)頁(yè)的正確源代碼已經(jīng)在里面了
現在我們仔細研究一下源代碼就可以看到,我們需要的內容在下面這個(gè)位置

現在正確的源代碼也有了,內容的位置也知道了,直接解析,爬取,完事就好了。

想得美,經(jīng)過(guò)這樣的爬取之后,對內容進(jìn)行解析,讓我們看看究竟爬到?jīng)]有

from lxml import etreeimport rehtml=etree.HTML(driver.page_source)links=html.xpath('//div[@class='reader-pic-item']/@style')part = re.compile(r'url[(](.*?)[)]')qa=''.join(links)z=part.findall(qa)z
我們可以知道,其實(shí)我們只爬到 3 張 PDF,其他的都沒(méi)有爬到。
這是為什么呢?
這是百度文庫為了防止大家去爬,專(zhuān)門(mén)設置的一個(gè)小機關(guān)

返回百度文庫,我們仔細看看源代碼,其實(shí)我們可以發(fā)現,隨著(zhù)頁(yè)面的變化,源代碼是不斷改變的,每次都只有 3 張圖片的 url。
并且這個(gè)頁(yè)碼數也有一定的規律,如果在第二頁(yè),那么圖片就是 1,2,3,如果在第三頁(yè),圖片就是 2,3,4

那么我們的疑惑一下就解決了,只需要不斷地進(jìn)行換頁(yè)的爬取,就可以了
接下來(lái)就是如何實(shí)現換頁(yè)的操作了
這個(gè)需要兩個(gè)步驟,先是點(diǎn)擊繼續閱讀,然后進(jìn)行頁(yè)面輸入實(shí)現換頁(yè)。
先實(shí)現點(diǎn)擊的操作,代碼如下:
button = driver.find_element_by_xpath('//*[@id='html-reader-go-more']/div[2]/div[1]/span')button.click()driver.execute_script('arguments[0].click();', button)整個(gè)操作是通過(guò) JS 來(lái)進(jìn)行的,大家可以把這個(gè)記住,以后需要點(diǎn)擊的時(shí)候直接用就可以
然后就是輸入頁(yè)面實(shí)現換頁(yè),這個(gè)其實(shí)涉及的比較多,細分的話(huà),步驟分為獲取總頁(yè)數,依次輸入頁(yè)面并點(diǎn)擊。
import re# 尋找頁(yè)面source = re.compile(r'<span class='page-count'>/(.*?)</span>')number = int(source.findall(driver.page_source)[0])# 輸入頁(yè)面并點(diǎn)擊driver.find_element_by_class_name('page-input').clear()driver.find_element_by_class_name('page-input').send_keys('2')driver.find_element_by_class_name('page-input').send_keys(Keys.ENTER)如果小伙伴成功實(shí)現了上面的操作,其實(shí)大體的爬取工作已經(jīng)差不多了,接下來(lái)就是保存我們的 PPT 和 PDF了

因為爬取 PDF 和 PPT 的時(shí)候,我們是爬取的圖片的源地址,那么我們要獲得這張圖片并保存下來(lái)就必須對這個(gè)地址發(fā)起請求,然后將返回頭以二進(jìn)制保存下來(lái)。
for m in range(3): pic = requests.get(z[m]).content # 方法一# file = open(f'./照片/{m+1}.jpg','wb')# file.write(pic)# file.close() # 方法二 with open(f'./照片/{m+1}.jpg','wb') as f: f.write(pic) f.close()在這里,提醒大家一下一定要按照對圖片用正確順序進(jìn)行命名,因為后面保存為 PDF 的時(shí)候,需要排序
在 py 文件的目錄下,大家就可以看見(jiàn)保存下來(lái)的圖片了
最后一步,將圖片保存為 PDF
from PIL import Imageimport osfolderPath = 'F:/TEST'filename = 'test'files = os.listdir(folderPath)jpgFiles = []sources = []for file in files: if 'jpg' in file: jpgFiles.append(file)tep = []for i in jpgFiles: ex = i.split('.') tep.append(int(ex[0]))tep.sort()jpgFiles=[folderPath +'/'+ str(i) + '.jpg' for i in tep]output = Image.open(jpgFiles[0])jpgFiles.pop(0)for file in jpgFiles: img = Image.open(file) img = img.convert('P') sources.append(img)output.save(f'./{filename}.pdf','PDF',save_all=True,append_images=sources)最終的結果就是生成了咱們的 PDF 文件

上述的操作看起來(lái)很多,很麻煩,其實(shí)并不是的,因為大部分的操作都是固定的,大家只需要記熟就可以了。
簡(jiǎn)單的 GUI 制作

GUI 這塊,我們整了點(diǎn)新活兒,用 C# 編寫(xiě) winform 簡(jiǎn)易的 GUI,調用爬蟲(chóng)的 Py thon 代碼

C# 調用 python 項目的方式我簡(jiǎn)單用 Process 類(lèi)執行,通過(guò)執行 python.exe 執行代碼
public static void RunPythonScript(string sArgName, string py, string args = '', params string[] teps){ Process p = new Process(); //(沒(méi)放debug下,寫(xiě)絕對路徑) //string path = @'C:\Users\zll\Desktop\baidu\CSharpCallPython\bin\Debug\' + sArgName; // 獲得python文件的絕對路徑(將文件放在c#的debug文件夾中可以這樣操作) string path = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + sArgName; //沒(méi)有配環(huán)境變量的話(huà),可以像我這樣寫(xiě)python.exe的絕對路徑。如果配了,直接寫(xiě)'python.exe'即可 //p.StartInfo.FileName = @'C:\Users\zll\AppData\Local\Programs\Python\Python37-32\python.exe'; p.StartInfo.FileName = @py; string sArguments = path; foreach (string sigstr in teps) { sArguments += ' ' + sigstr;//傳遞參數 } sArguments += ' ' + args; p.StartInfo.Arguments = sArguments; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardInput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.CreateNoWindow = true; p.Start(); p.BeginOutputReadLine(); p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); Console.ReadLine(); p.WaitForExit();}對輸入 的內容進(jìn)行簡(jiǎn)單判斷,如果不是百度文庫地址或不是 python.exe 地址,報錯
if (!url.Contains('https://wenku.baidu.com/view/')){ MessageBox.Show('請輸入正確的百度文庫網(wǎng)址!'); lbl_state.Text = '請重新輸入。';}else if (!py.Contains('python.exe')){ MessageBox.Show('請輸入正確的python.exe路徑!'); lbl_state.Text = '請重新輸入。';}else{ //輸入參數列表 String[] strArr = new String[] { url, istxt }; string sArguments = @'src\wenku.py';//這里是python的文件名字 RunPythonScript(sArguments, py, '-u', strArr); if(result.Contains('0')) lbl_state.Text = '對不起,爬取失敗。'; else if (result.Contains('1')) lbl_state.Text = '爬取成功!';}因為 GUI 部分比較簡(jiǎn)單,這里就不過(guò)多描述了。
聯(lián)系客服