操作環境
- 編譯器:pycharm社區版
- python 版本:anaconda python3.7.4
- 瀏覽器選擇:Google瀏覽器
- 需要用到的第三方模塊:requests , lxml , selenium , time , bs4,os
網頁分析
明確目標
首先我們打開騰訊動漫首頁,分析要抓取的目標漫畫。
找到騰訊動漫的漫畫目錄頁,簡單看了一下目錄,發現全站的漫畫數量超過了三千部(感覺就是爬下來也會把內存撐爆)
於是我覺得爬取首頁的推薦漫畫會是一個比較好的選擇(爬取全站漫畫只需要稍稍改一下網址構造就可以做到了)
提取漫畫地址
選定了對象之后,就應該想辦法來搞到漫畫的地址了
右擊檢查元素,粗略看一遍網頁的源代碼,這時我發現里面有很多連續的

隨便打開一個《li》標簽,點擊里面包裹的鏈接地址會跳轉到一個新的網頁,這個網頁正是我想要找的漫畫地址,可以見得我的猜測是正確的,等到實際操作的時候再用表達式提取信息就非常容易了
提取漫畫章節地址
進入漫畫的目錄頁,發現一頁最多可以展示20章的漫畫目錄,要想更換顯示還需要點擊章節名上面的選項卡來顯示其他章節的地址
接下來就需要我們來檢查網頁元素想辦法來獲取章節地址了,同樣右擊檢查元素
在看到了源代碼后,我發現了一個非常驚喜的事情,這個源碼里面包含這所有的章節鏈接,而不是通過動態加載來展示的,這就省去了我們提取其他章節鏈接的功夫,只需要花心思提取漫畫圖片就可以了
這里每個《p》標簽下包含了五個《a》標簽,每個《li》標簽下包含了四個《p》標簽,而每個漫畫的鏈接就存在每個《a》標簽中,可以輕松通過語法來提取到每頁的鏈接信息
提取漫畫圖片
怎么將漫畫的圖片地址提取出來並保存到本地,這是這個代碼的難點和核心
先是打開漫畫,這個漫畫頁應該是被加上了某些措施,所以它沒辦法使用右鍵查看網頁源代碼,但是使用快捷鍵[ctrl + shift +i]是可以看到的
按下[ctrl + shift + i],檢查元素
通過第一次檢查,可以發現網頁的元素中只有前幾張圖片的地址信息,后面的信息都為后綴.gif的文件表示,這些gif文件就是圖片的加載動畫
接着向下滑動到底部,等待圖片全部顯示出來再次檢查元素
現在所有的漫畫圖片全部顯示出來,下方並無.gif 的文件,由此可知,騰訊動漫是以js異步加載來顯示圖片的,要想獲取頁面的全部圖片,就必須要滑動滾動條,將全部的圖片加載完成再進行提取,這里我選擇selenium模塊和chromedriver來幫助我完成這些操作。下面開始進行代碼的編寫。
編寫代碼
導入需要的模塊
import requests
from lxml import etree
from selenium import webdriver #selenium模擬操作
from time import sleep
from bs4 import BeautifulSoup
from selenium.webdriver.chrome.options import Options #谷歌無頭瀏覽器
import os
獲取漫畫地址
這里我使用的是xpath提取漫畫地址信息,在谷歌瀏覽器中使用xpath helper插件輔助編寫xpath表達式
#打開騰訊動漫首頁
url = 'https://ac.qq.com/'
#給網頁發送請求
data = requests.get(url).text
#將網頁信息轉換成xpath可識別的類型
html = etree.HTML(data)
#提取到每個漫畫的目錄頁地址
comic_list = html.xpath('//a[@class="in-rank-name"]/@href')
print(comic_list)
print一下輸出的comic_list,提取成功
提取漫畫的內容頁
內容頁的提取也很簡單,就像上面的分析一樣,使用簡單的xpath語法即可提取
然后我們再將漫畫的名字提取出來,方便為保存的文件夾命名
#遍歷提取到的信息
for comic in comic_list:
#拼接成為漫畫目錄頁的網址
comic_url = url + str(comic)
#從漫畫目錄頁提取信息
url_data = requests.get(comic_url).text
#准備用xpath語法提取信息
data_comic = etree.HTML(url_data)
#提取漫畫名--text()為提取文本內容
name_comic = data_comic.xpath("//h2[@class='works-intro-title ui-left']/strong/text()")
#提取該漫畫每一頁的地址
item_list = data_comic.xpath("//span[@class='works-chapter-item']/a/@href")
print(name_comic)
print(item_list)
print打印的信息:
提取章節名
剛剛我們輸出的是漫畫頁的地址字段,但是通過這些字段並不能請求到信息,還需在前面加上域名才可以構成一個完整的網址
提取章節名是為了在漫畫名的文件夾下再為每個章節創建一個文件夾保存漫畫圖片
for item in item_list:
#拼接每一章節的地址
item_url = url + str(item)
#print(item_url)
#請求每一章節的信息
page_mes = requests.get(item_url).text
#准備使用xpath提取內容
page_ming = etree.HTML(page_mes)
#提取章節名
page_name = page_ming.xpath('//span[@class="title-comicHeading"]/text()')
print(page_name)
打印章節名:
獲取漫畫源網頁代碼
這個部分的代碼是這個代碼的核心部分,也是花費時間最久的部分
首先我們知道通過正常的方式沒有辦法請求到所有的圖片地址信息,若是使用抓包方法會變得非常難分析,所以我采用的是模擬瀏覽器滑動的方法來獲得圖片的地址信息
為了方便看到結果,先將webdriver設置為有界面模式,等到實現想要的功能之后,再將它隱藏起來
#webdriver位置
path = r'/home/jmhao/chromedriver'
#瀏覽器參數設置
browser = webdriver.Chrome(executable_path=path)
#開始請求第一個章節的網址
browser.get(item_url)
#設置延時,為后續做緩沖
sleep(2)
#嘗試執行下列代碼
try:
#設置自動下滑滾動條操作
for i in range(1, 100):
#滑動距離設置
js = 'var q=document.getElementById("mainView").scrollTop = ' + str(i * 1000)
#執行滑動選項
browser.execute_script(js)
#延時,使圖片充分加載
sleep(2)
sleep(2)
#將打開的界面截圖保存,證明無界面瀏覽器確實打開了網頁
browser.get_screenshot_as_file(str(page_name) + ".png")
#獲取當前頁面源碼
data = browser.page_source
#在當前文件夾下創建html文件,並將網頁源碼寫入
fh = open("dongman.html", "w", encoding="utf-8")
#寫入操作
fh.write(data)
#關掉瀏覽器
fh.close()
# 若上述代碼執行報錯(大概率是由於付費漫畫),則執行此部分代碼
except Exception as err:
#跳過錯誤代碼
pass
運行之后會自動打開漫畫的內容頁,並拖動右側的滑動條(模擬了手動操作,緩慢拖動是為了讓圖片充分加載),其中的sleep方法和網速有一定的關系,網速好的可以適當減少延時的時間,網速差可適當延長
在寫拖動滑動條的代碼時,我嘗試了非常多種拖動寫法,也模擬了按下方向鍵的操作,可是只有這一種方法使用成功了。我認為失敗的原因可能是剛打開界面的時候會有一個導航條擋住滑塊,導致無法定位到滑塊的坐標(因為我用其他網頁測試的時候都是可以拖動的)
使用的try是為了防止有一些章節會彈出付費窗口,導致程序報錯,使后續無法運行,即遇到會報錯的情況就跳過此段代碼,執行except中的選項
這段程序運行完之后有一個dongman.html文件保存在當前文件夾下,里面就包含了所有圖片的url,接下來只要讀取這個文件的內容就可以提取到所有的漫畫地址了
下載漫畫圖片
當我們保存完網頁的源代碼之后,接下來的操作就變得簡單了 我們要做的就是提取文件內容,將圖片下載到本地
#用beautifulsoup打開本地文件
html_new = BeautifulSoup(open('dongman.html', encoding='utf-8'), features='html.parser')
#提取html文件中的主體部分
soup = html_new.find(id="mainView")
#設置變量i,方便為保存的圖片命名
i = 0
#提取出主體部分中的img標簽(因為圖片地址保存在img標簽中)
for items in soup.find_all("img"):
#提取圖片地址信息
item = items.get("src")
#請求圖片地址
comic_pic = requests.get(item).content
#print(comic_pic)
#嘗試提取圖片,若發生錯誤則跳過
try:
#打開文件夾,將圖片存入
with open('comic/' + str(name_comic) + '/' + str(page_name) + '/' + str(i + 1) + '.jpg', 'wb') as f:
#print('正在下載第 ', (i + 1), ' 張圖片中')
print('正在下載' , str(name_comic) , '-' , str(page_name) , '- 第' , (i+1) , '張圖片')
#寫入操作
f.write(comic_pic)
#更改圖片名,防止新下載的圖片覆蓋原圖片
i += 1
#若上述代碼執行報錯,則執行此部分代碼
except Exception as err:
#跳過錯誤代碼
pass
下載結果
到了這里代碼就寫完了,來看一下運行結果:
打開文件夾看到:
完整代碼
import requests
from lxml import etree
from selenium import webdriver
from time import sleep
from bs4 import BeautifulSoup
from selenium.webdriver.chrome.options import Options
import os
#打開騰訊動漫首頁
url = 'https://ac.qq.com/'
#給網頁發送請求
data = requests.get(url).text
#將網頁信息轉換成xpath可識別的類型
html = etree.HTML(data)
#提取到每個漫畫的目錄頁地址
comic_list = html.xpath('//a[@class="in-rank-name"]/@href')
#print(comic_list)
#遍歷提取到的信息
for comic in comic_list:
#拼接成為漫畫目錄頁的網址
comic_url = url + str(comic)
#從漫畫目錄頁提取信息
url_data = requests.get(comic_url).text
#准備用xpath語法提取信息
data_comic = etree.HTML(url_data)
#提取漫畫名--text()為提取文本內容
name_comic = data_comic.xpath("//h2[@class='works-intro-title ui-left']/strong/text()")
#提取該漫畫每一頁的地址
item_list = data_comic.xpath("//span[@class='works-chapter-item']/a/@href")
# print(name_comic)
# print(item_list)
#以漫畫名字為文件夾名創建文件夾
os.makedirs('comic/' + str(name_comic))
#將一本漫畫的每一章地址遍歷
for item in item_list:
#拼接每一章節的地址
item_url = url + str(item)
#print(item_url)
#請求每一章節的信息
page_mes = requests.get(item_url).text
#准備使用xpath提取內容
page_ming = etree.HTML(page_mes)
#提取章節名
page_name = page_ming.xpath('//span[@class="title-comicHeading"]/text()')
#print(page_name)
#再以章節名命名一個文件夾
os.makedirs('comic/' + str(name_comic) + '/' + str(page_name))
#以下為代碼的主體部分
#設置谷歌無界面瀏覽器
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
#webdriver位置
path = r'/home/jmhao/chromedriver'
#瀏覽器參數設置
browser = webdriver.Chrome(executable_path=path, options=chrome_options)
#開始請求第一個章節的網址
browser.get(item_url)
#設置延時,為后續做緩沖
sleep(2)
#browser.get_screenshot_as_file(str(page_name) + ".png")
#嘗試執行下列代碼
try:
#設置自動下滑滾動條操作
for i in range(1, 100):
#滑動距離設置
js = 'var q=document.getElementById("mainView").scrollTop = ' + str(i * 1000)
#執行滑動選項
browser.execute_script(js)
#延時,使圖片充分加載
sleep(2)
sleep(2)
#將打開的界面截圖保存,證明無界面瀏覽器確實打開了網頁
browser.get_screenshot_as_file(str(page_name) + ".png")
#獲取當前頁面源碼
data = browser.page_source
#在當前文件夾下創建html文件,並將網頁源碼寫入
fh = open("dongman.html", "w", encoding="utf-8")
#寫入操作
fh.write(data)
#關掉無界面瀏覽器
fh.close()
#下面的操作為打開保存的html文件,提取其中的圖片信息,並保存到文件夾中
#用beautifulsoup打開本地文件
html_new = BeautifulSoup(open('dongman.html', encoding='utf-8'), features='html.parser')
#提取html文件中的主體部分
soup = html_new.find(id="mainView")
#設置變量i,方便為保存的圖片命名
i = 0
#提取出主體部分中的img標簽(因為圖片地址保存在img標簽中)
for items in soup.find_all("img"):
#提取圖片地址信息
item = items.get("src")
#請求圖片地址
comic_pic = requests.get(item).content
#print(comic_pic)
#嘗試提取圖片,若發生錯誤則跳過
try:
#打開文件夾,將圖片存入
with open('comic/' + str(name_comic) + '/' + str(page_name) + '/' + str(i + 1) + '.jpg', 'wb') as f:
#print('正在下載第 ', (i + 1), ' 張圖片中')
print('正在下載' , str(name_comic) , '-' , str(page_name) , '- 第' , (i+1) , '張圖片')
#寫入操作
f.write(comic_pic)
#更改圖片名,防止新下載的圖片覆蓋原圖片
i += 1
#若上述代碼執行報錯,則執行此部分代碼
except Exception as err:
#跳過錯誤代碼
pass
# 若上述代碼執行報錯(大概率是由於付費漫畫),則執行此部分代碼
except Exception as err:
#跳過錯誤代碼
pass
github:https://github.com/jjjjmhao/Sprider/commit/16d15a34d722f3c50060f9f3015c9ff577deb05a