python爬蟲教程(二)動態網頁抓取
- 決議真實地址抓取
- 通過selenium 模擬瀏覽器抓取
- selenium 安裝與測驗
- selenium爬取一條評論
- selenium獲取文章的所有評論
- selenium其他操作
參考鏈接
目的是爬取所有評論,爬取的鏈接下面有提到
如果使用 AJAX 加載的動態網頁,有兩種方法爬取:
- 通過瀏覽器審查元素決議地址
- 通過selenium模擬瀏覽器抓取
以下分別介紹兩種方法:(對代碼有疑問歡迎提出改進)
決議真實地址抓取
例子為參考鏈接中提供的網址,需要爬取網站的評論的鏈接
- 抓包,點擊Network,重繪網頁,評論資料就在這些檔案中,一般而言,這些資料可能以 json 檔案格式獲取,然后找到評論資料檔案,見下圖,點擊 Preview 即可查看資料,

- 爬取資料,注釋有解釋,執行一遍有列印測驗結果,
import requests
import json
link = """https://api-zero.livere.com/v1/comments/list?callback=jQuery1124027946415311453476_1602502907332&limit=10&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1602502907334"""
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'}
r = requests.get(link, headers=headers)
print(r.text)
# 獲取 json 的 string
json_string = r.text
# 將文本中的json提取出來
json_string = json_string[json_string.find('{'):-2]
# 測驗是否提取成功
print(json_string)
json_data = json.loads(json_string)
comment_list = json_data['results']['parents']
for eachone in comment_list:
message = eachone['content']
print(message)

- 改進:這里僅僅爬取了第一頁的評論,我們需要爬取所有的評論,點擊不同的頁碼,會發現多了一些json檔案:

點擊這些Json檔案,對比他們的URL,會發現有一個引數不同:

這個引數代表的就是頁碼數,offset=1代表第一頁,(注意:如果第一次進入,可能沒有offset這個引數,因為offset默認為1,所以在URL中沒有展示)
根據這個原理,撰寫最終代碼如下:
# 通過瀏覽器審查元素決議地址,爬取所有評論
import requests
import json
# 爬取某一頁的評論,link為鏈接
def single_page_comment(link, page_number):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'}
r = requests.get(link, headers=headers)
# 獲取 json 的 string
json_string = r.text
# 將文本中的json提取出來
json_string = json_string[json_string.find('{'):-2]
# 測驗是否提取成功
# print(json_string)
json_data = json.loads(json_string)
comment_list = json_data['results']['parents']
print(" 第 %g 頁評論:" % page_number)
for eachone in comment_list:
message = eachone['content']
print(message)
print()
# 鏈接前半部分
link1 = """https://api-zero.livere.com/v1/comments/list?callback=jQuery1124027946415311453476_1602502907332&limit=10&offset="""
# 鏈接后半部分
link2 = """&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1602502907334"""
for page in range(1, 11):
# 鏈接進行拼接,得到不同頁評論的URL
current_link = link1 + str(page) + link2
single_page_comment(current_link, page)
通過selenium 模擬瀏覽器抓取
在之前的方法中, 有些網站為了規避這些抓取會對地址進行加密. 因此可以用第二種方法
selenium 安裝與測驗
- 首先安裝selenium 庫:
pip install selenium
- 安裝瀏覽器對應的geckodriver,我使用的是chrome,所以根據自己的chrome版本下載,下載地址chrome geckodriver
如果使用的是火狐,下載地址為此
其他瀏覽器可以百度
- 測驗,代碼如下:
from selenium import webdriver
# 下載的geckodriver的存盤位置
driver = webdriver.Chrome(executable_path='D:\\12102\\files\\chromedriver.exe')
# 自動訪問的網站
driver.get("https://www.baidu.com/")
結果如下:

selenium爬取一條評論
修改代碼,更改打開的網站:driver.get("http://www.santostang.com/2018/07/04/hello-world/")
可以定位到評論的文字:

通過下面代碼來爬取該資料,注意評論在一個iframe框架下面,因此要先對iframe進行決議,因此先使用switch_to轉移焦點,
from selenium import webdriver
# 下載的geckodriver的存盤位置
driver = webdriver.Chrome(executable_path='D:\\12102\\files\\chromedriver.exe')
# 自動訪問的網站
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
# 錯誤范例,注意函式的名稱,elements與element的區別
# 爬取一條評論
# switch_to相當于轉移焦點
# driver.switch_to.frame(driver.find_elements_by_css_selector("iframe[title='livere-comment']"))
# comment = driver.find_elements_by_css_selector("div.reply-content")
# content = comment.find_element_by_tag_name('p')
# print(content.text)
# 爬取一條評論
# switch_to相當于轉移焦點
driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere-comment']"))
driver.implicitly_wait(10) # 隱性等待10秒
comment = driver.find_element_by_css_selector('div.reply-content')
content = comment.find_element_by_tag_name('p')
print(content.text)
這里添加了driver.implicitly_wait(10) ,隱性等待10秒,如果沒有加這行代碼,會報錯找不到div.reply-content,因為iframe框架加載需要時間,
至于driver.implicitly_wait(10)與time.sleep(10)的區別,見該文章
selenium獲取文章的所有評論
同樣是之前的網站,這次爬取所有的評論,觀察網頁的結構

每頁有10小頁,瀏覽完10頁后,點擊下一頁,總共有27頁,所有用一個嵌套for回圈來完成,外面一層分別表示1-10,11-20,21-27頁,里面一層輸出每一頁的評論,每頁評論輸出方法同之前的爬取一條評論,
代碼見下,有注釋:
# selenium獲取文章的所有評論
from selenium import webdriver
# 下載的geckodriver的存盤位置
driver = webdriver.Chrome(executable_path='D:\\12102\\files\\chromedriver.exe')
# 自動訪問的網站
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
# 需要提前知道ii的范圍,ii指的是需要翻幾次頁,在我寫這個代碼時,評論有27頁,意味著要點兩次下一頁,因為每頁有10小頁,所以ii為3
for ii in range(0, 3):
# i指的是每頁有10小頁
for i in range(0, 10):
# 下滑到頁面底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 爬取某一頁的所有評論
driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere-comment']"))
driver.implicitly_wait(10) # 隱性等待10秒
comment = driver.find_elements_by_css_selector('div.reply-content')
print()
print("第 %g 頁評論:" % int(i + 1 + ii * 10))
# 列印所有評論
for eachcomment in comment:
content = eachcomment.find_element_by_tag_name('p')
print(content.text)
# 獲取所有的頁碼按鈕
page_btn = driver.find_elements_by_class_name("page-btn")
# 統計這一頁總共有多少頁評論,默認最多為10頁
page_btn_size = len(page_btn)
if i == page_btn_size - 1:
driver.switch_to.default_content()
driver.implicitly_wait(10)
break
# 按順序點擊某一頁
if i != 9 and i + 1 < page_btn_size:
page_btn[i + 1].click()
# 把iframe又轉回去,注意加上這一句
driver.switch_to.default_content()
driver.implicitly_wait(10)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere-comment']"))
# 判斷頁面是否有下一頁的按鈕,沒有就退出
try:
next_page = driver.find_element_by_class_name("page-last-btn")
next_page.click()
# 把iframe又轉回去,注意加上這一句
driver.switch_to.default_content()
driver.implicitly_wait(10)
except:
print()
print("爬取結束!(不是爬取內容)")
selenium其他操作
- 自動登錄
user = driver.find_element_by_name("username") #找到用戶名輸入框
user.clear #清除用戶名輸入框內容
user.send_keys("1234567") #在框中輸入用戶名
pwd = driver.find_element_by_name("password") #找到密碼輸入框
pwd.clear #清除密碼輸入框內容
pwd.send_keys("******") #在框中輸入密碼
driver.find_element_by_id("loginBtn").click() #點擊登錄
- 一般來說,Selenium因為要把整個網頁加載出來,再開始爬取內容,速度往往較慢,但是可以用以下方法:
禁用圖片,CSS,JS,如下圖結果:

chrome例子:
from selenium import webdriver
options = webdriver.ChromeOptions()
# 禁用圖片,CSS,JS
prefs = {
'profile.default_content_setting_values': {
'images': 2,
'permissions.default.stylesheet': 2,
'javascript': 2
}
}
options.add_experimental_option('prefs', prefs)
# 下載的geckodriver的存盤位置
driver = webdriver.Chrome(executable_path='D:\\12102\\files\\chromedriver.exe', options=options)
# 自動訪問的網站
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
firefox例子:
# 控制 css
from selenium import webdriver
fp = webdriver.FirefoxProfile()
fp.set_preference("permissions.default.stylesheet",2)
driver = webdriver.Firefox(firefox_profile=fp, executable_path = r'C:\Users\santostang\Desktop\geckodriver.exe')
#把上述地址改成你電腦中geckodriver.exe程式的地址
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
在上述代碼中,控制css的加載主要用fp = webdriver.FirefoxProfile()這個功能,設定不加載css,使用fp.set_preference(“permissions.default.stylesheet”,2),之后使用webdriver.Firefox(firefox_profile=fp)就可以控制不加載css了,運行上述代碼,得到的頁面如下所示,
# 限制圖片的加載
from selenium import webdriver
fp = webdriver.FirefoxProfile()
fp.set_preference("permissions.default.image",2)
driver = webdriver.Firefox(firefox_profile=fp, executable_path = r'C:\Users\santostang\Desktop\geckodriver.exe')
#把上述地址改成你電腦中geckodriver.exe程式的地址
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
與限制css類似,限制圖片的加載可以用fp.set_preference(“permissions. default.image”,2),運行上述代碼,得到的頁面如圖所示,
# 限制 JavaScript 的執行
from selenium import webdriver
fp = webdriver.FirefoxProfile()
fp.set_preference("javascript.enabled", False)
driver = webdriver.Firefox(firefox_profile=fp, executable_path = r'C:\Users\santostang\Desktop\geckodriver.exe')
#把上述地址改成你電腦中geckodriver.exe程式的地址
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/211229.html
標籤:其他
