寫在前面的話:切記切記要設定休眠,不能頻繁請求
小白的一點案例記錄,望大神們手下留情。。。
共兩部分原始碼分別見3.1和3.2
日常辛苦工(mo)作(yu)之後的某時,心血來潮想查下以前離職公司現在怎麼樣了,於是各種企業資訊查詢,某查查登場,註冊–>驗證–>繫結–>登入,還好可以看了,猛然眼前一亮,訴訟異常,悲(竊)傷(喜)著點開,會員,看不到全部內容,咱也理解,畢竟人家是公司不是盈利機構,於是乎,就有了本文的初始念頭:我自己取資料自己查詢自己分析!
再叨叨一句:小心,爬著爬著就進去了!
用selenium模擬比較真實一些,慢就慢點無所謂
環境:win10、python3.7
工具:anaconda spyder、chrome driver
三方包:selenium、pandas、bs4、requests、random
分析了下網站結構,
1、首頁的搜尋按鈕必須輸入關鍵詞才能搜尋,不同關鍵詞的搜尋結果數量不一;
2、搜尋「0」出現的案例條數,與首頁下方的案例點選後相加得到的條數一致;
3、列表頁的標題行固定為class="fd-list-01"
;
4、標題頁沒有進入詳情頁的連線;且點選標題後,新視窗開啟詳情頁;
5、開啟詳情頁後發現,詳情頁連線較統一,拼接變數為文章型別和文章ID;
6、列表頁標題中的onclick即有此兩個變數;
7、測試驗證上述5和6成功;
8、湊個數吧;
先上原始碼:
# -*- coding: utf-8 -*-
"""
Created on Thu Sep 24 16:52:53 2020
@author: janlyn
"""
from selenium import webdriver
import time
import pandas as pd
import random
driver = webdriver.Chrome()
driver.get(圖片)
driver.find_element_by_id('fd-search').send_keys('0')
driver.find_element_by_class_name('fd-btn').click()
driver.switch_to.window(driver.window_handles[1])
# 獲取列表頁資訊
def get_data():
items = driver.find_elements_by_class_name('fd-list-01')
for ele in items[1:]:
titles = ele.text
a = ele.find_element_by_tag_name('a')
# 獲取onlick中的內容
alls = a.get_attribute('onclick')
if alls:
# 提取onlick中的內容,完整內容為
# οnclick="ckxq('開庭公告','D5EA00BE1700316F2AA7B7C5EEF535F5')"
li = alls.replace("ckxq('","").replace("')","").split("','")
li.append(titles)
al.append(li)
# 點選下一頁
def click_next():
pages = driver.find_element_by_class_name('pageBtnWrap').find_elements_by_tag_name('a')
for page in pages:
title = page.get_attribute('title')
if title == '下一頁':
page.click()
# 若失敗則重新執行當前頁
def loop_temp(func,page):
reg = 1
while reg == 1:
try:
func
reg = 0
print('{}執行成功'.format(page))
except:
print('{}執行失敗,再來一次'.format(page))
reg = 1
# 欄位頭,type和id需要構造詳情頁url,title只是順手取出來,方便驗證
al = [['type','id','title']]
# 迴圈遍歷全部頁數
for i in range(1,28):
loop_temp(get_data(),i)
reg = 1
# 判斷獲取的資料是否為當前遍歷頁資料
while reg == 1:
loop_temp(click_next(),i)
time.sleep(random.randrange(50,150,10)/100)
curr = driver.find_element_by_class_name('curr').text
if curr == str(i+1):
reg = 0
# 最後一頁直接獲取資料
get_data()
# 轉為df資料框,方便操作
df_te = pd.DataFrame(te[1:],columns=te[0])
# 去重資料
df_te_tmp = df_te.drop_duplicates()
# 儲存列表資料
df_te_tmp.to_excel('spyder.xlsx',index=False)
直接儲存執行肯定會出錯滴,你得偵錯吶,這又不是成品原始碼;
思路如下:
driver = webdriver.Chrome()
.get()
方法開啟連結
driver.get(圖片)
.send_keys()
方法輸入內容
driver.find_element_by_id('fd-search').send_keys('0')
.click()
方法進行點選
driver.find_element_by_class_name('fd-btn').click()
可以通過driver.title
檢視當前tab標題
可以通過driver.refresh()
重新整理當前tab
driver.window_handles
獲取tab控制程式碼
.switch_to.window()
根據控制程式碼進行切換
driver.switch_to.window(driver.window_handles[1])
.get_attribute()
可獲取當前元素中的指定屬性值,前文述的型別和id均可在’onlick’中進行獲取
# 獲取列表頁資訊
def get_data():
items = driver.find_elements_by_class_name('fd-list-01')
for ele in items[1:]:
titles = ele.text
a = ele.find_element_by_tag_name('a')
# 獲取onlick中的內容
alls = a.get_attribute('onclick')
if alls:
# 提取onlick中的內容,完整內容為
# οnclick="ckxq('開庭公告','D5EA00BE1700316F2AA7B7C5EEF535F5')"
li = alls.replace("ckxq('","").replace("')","").split("','")
li.append(titles)
al.append(li)
迴圈class
為pageBtnWrap
的a
標籤,並判斷title
的值是否為下一頁
,若是則點選
# 點選下一頁
def click_next():
pages = driver.find_element_by_class_name('pageBtnWrap').find_elements_by_tag_name('a')
for page in pages:
title = page.get_attribute('title')
if title == '下一頁':
page.click()
點選下一頁或獲取列表頁失敗時,需要重新嘗試
# 若失敗則重新執行當前頁
def loop_temp(func,page):
reg = 1
while reg == 1:
try:
func
reg = 0
print('{}執行成功'.format(page))
except:
print('{}執行失敗,再來一次'.format(page))
reg = 1
已知失敗原因如下:
1)點選下一頁後實際未點選(可能頁面未載入完成導致),導致重複採集;
2)點選下一頁後,頁面未載入完畢,導致頁面屬性缺失,從而獲取不到列表頁相關資訊;
3)點選過快導致頁面載入失敗;
pd可以直接將二維陣列轉化為資料框,故將提取的資料存為列表,先上欄位表頭
# 欄位頭,type和id需要構造詳情頁url,title只是順手取出來,方便驗證
al = [['type','id','title']]
並回圈遍歷每一頁進行採集
最後一頁時多呼叫一次列表資料,而不需要進入迴圈
# 迴圈遍歷全部頁數
for i in range(1,28):
loop_temp(get_data(),i)
reg = 1
# 判斷獲取的資料是否為當前遍歷頁資料
while reg == 1:
loop_temp(click_next(),i)
time.sleep(random.randrange(50,150,10)/100)
curr = driver.find_element_by_class_name('curr').text
if curr == str(i+1):
reg = 0
# 最後一頁直接獲取資料
get_data()
# 轉為df資料框,方便操作
df_te = pd.DataFrame(te[1:],columns=te[0])
# 去重資料
df_te_tmp = df_te.drop_duplicates()
# 儲存列表資料,到當前工作目錄下
df_te_tmp.to_excel('spyder.xlsx',index=False)
上原始碼
在這裡插入程式碼片
detail = pd.read_excel('spyder.xlsx')
type_dic = {圖片}
url = type_dic[detail_type]
url = '{}?id={}'.format(url,detail_id)
此處有個工具,詳見:curl轉python
def get_detail_spyder(detail_type = '案例',detail_id = 'ff8080815a5ef25f015a7407287c1b3a',title='列表標題'):
# BeautifulSoup 解析
headers = {
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Referer': 圖片,
'Accept-Language': 'zh-CN,zh;q=0.9',
}
# params = (('id', detail_id),)
url = type_dic[detail_type]
url = '{}?id={}'.format(url,detail_id)
try:
# 隨機休眠0.5-1.5秒
time.sleep(random.randrange(50,150,10)/100)
# response = requests.get(url, headers=headers, params=params)
response = requests.get(url, headers=headers)
bsobj = BeautifulSoup(response.text,'lxml')
# 標題正文等合體
bs_all = bsobj.find(attrs={"class":"fd-fix"})
# 文章標題
title_name = bs_all.findChild().text
# 文章標題下方的文章資訊,待解析
title_info = bs_all.findChild().findNext().text
fabudanwei,suoshushengfen,fabushijian,liulanliang=('','','','')
# 有的文章無該4個欄位中的某個,故迴圈遍歷匹配提取
for i in title_info.split('\u3000\u3000'):
if '釋出單位' in i:
fabudanwei = i.split(':')[1]
elif '所屬省份' in i:
suoshushengfen = i.split(':')[1]
elif '釋出時間' in i:
fabushijian = i.split(':')[1]
elif '瀏覽量' in i:
liulanliang = i.split(':')[1]
zhengwen_html = str(bs_all.find(attrs={"class":"fd-alt-all"}))
# 清除style標籤
[s.extract() for s in bs_all("style")]
zhengwen = bs_all.find(attrs={"class":"fd-alt-all"}).text
# 構造並返回字典
dic = {'型別':detail_type,'標題':title_name,'列表標題':title,'資訊':title_info,\
'釋出單位':fabudanwei,'所屬省份':suoshushengfen,'釋出時間':fabushijian,\
'瀏覽量':liulanliang,'正文html':zhengwen_html,'正文':zhengwen,'url':url}
return dic
except Exception as e:
print(url,e)
return 0
def get_detail(driver,detail_type = '案例',detail_id = 'ff8080815a5ef25f015a7407287c1b3a',title='列表標題'):
# selenium解析
url = type_dic[detail_type]
url = '{}?id={}'.format(url,detail_id)
driver.get(url)
# 隨機休眠0.5-1.5秒
time.sleep(random.randrange(50,150,10)/100)
try:
# 標題正文等合體
bs_all = driver.find_element_by_class_name('fd-fix')
# 文章標題
title_name = bs_all.find_element_by_tag_name('h2').text
# 文章標題下方的文章資訊,待解析
title_info = bs_all.find_element_by_tag_name('h5').text
fabudanwei,suoshushengfen,fabushijian,liulanliang=('','','','')
# 有的文章無該4個欄位中的某個,故迴圈遍歷匹配提取
for i in title_info.split('\u3000\u3000'):
if '釋出單位' in i:
fabudanwei = i.split(':')[1]
elif '所屬省份' in i:
suoshushengfen = i.split(':')[1]
elif '釋出時間' in i:
fabushijian = i.split(':')[1]
elif '瀏覽量' in i:
liulanliang = i.split(':')[1]
zhengwen_html = bs_all.find_element_by_class_name('fd-alt-all').get_attribute('outerHTML')
zhengwen = bs_all.find_element_by_class_name('fd-alt-all').text
# 構造並返回字典
dic = {'型別':detail_type,'標題':title_name,'列表標題':title,'資訊':title_info,\
'釋出單位':fabudanwei,'所屬省份':suoshushengfen,'釋出時間':fabushijian,\
'瀏覽量':liulanliang,'正文html':zhengwen_html,'正文':zhengwen,'url':url}
return dic
except Exception as e:
print(url,e)
return 0
leixing = 2
if leixing == 2:
driver = webdriver.Chrome()
for index,detail_type,detail_id,title,url in detail.itertuples():
print('正在獲取第{}條資料...,連線:{}'.format(index+1,url))
while True:
if leixing == 1:
dic = get_detail_spyder(detail_type,detail_id,title)
elif leixing == 2:
dic = get_detail(driver,detail_type,detail_id,title)
if dic:
result_list.append(dic)
break
result_df = pd.DataFrame(result_list)
見下篇