本文的文字及圖片來源于網路,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理,
以下文章來源于快學Python,作者 Corley
轉載地址
https://blog.csdn.net/fei347795790?t=1
專案概述
1.專案說明
本專案主要是對領導留言板內的所有留言的具體內容進行抓取,對留言詳情、回復詳情和評價詳情進行提取保存,并用于之后的資料分析和進一步處理,可以對政府的決策和電子政務的實施提供依據,網站鏈接是http://liuyan.people.com.cn/home?p=0,任意選擇一條留言點擊進入詳情頁后,如下
對于圖中標出的資料,均要進行爬取,以此構成一條留言的組成部分,
2.環境配置
(1)Python:3.x
(2)所需庫:
dateutil安裝方法:
pip install python-dateutil
selenium安裝方法:
pip install selenium
(3)模擬驅動:chromedriver,可點擊https://download.csdn.net/download/CUFEECR/12193208進行下載Google瀏覽器80.0.3987.16版對應版本,或點擊http://chromedriver.storage.googleapis.com/index.html下載與Google對應版本,并放入Python對應安裝路徑下的Scripts目錄下,
二、專案實施
1.匯入所需要的庫
import csv import os import random import re import time import dateutil.parser as dparser from random import choice from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.chrome.options import Options
主要匯入在爬取程序中需要用到的處理庫和selenium中要用到的類,
2.全域變數和引數配置
## 時間節點 start_date = dparser.parse('2019-06-01') ## 瀏覽器設定選項 chrome_options = Options() chrome_options.add_argument('blink-settings=imagesEnabled=false')
我們假設只爬取2019.6.1以后的留言,因為這之前的留言自動給好評,沒有參考價值,因此設定時間節點,并禁止網頁加載圖片,減少對網路的帶寬要求、提升加載速率,
3.產生隨機時間和用戶代理
def get_time(): '''獲取隨機時間''' return round(random.uniform(3, 6), 1) def get_user_agent(): '''獲取隨機用戶代理''' user_agents = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (iPod; U; CPU iPhone OS 2_1 like Mac OS X; ja-jp) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5F137 Safari/525.20", "Mozilla/5.0 (Linux;u;Android 4.2.2;zh-cn;) AppleWebKit/534.46 (KHTML,like Gecko) Version/5.1 Mobile Safari/10600.6.3 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" ] ## 在user_agent串列中隨機產生一個代理,作為模擬的瀏覽器 user_agent = choice(user_agents) return user_agent
產生隨機時間并隨機模擬瀏覽器用于訪問網頁,降低被服務器識別出是爬蟲而被禁的可能,
4.獲取領導的fid
def get_fid(): '''獲取所有領導id''' with open('url_fid.txt', 'r') as f: content = f.read() fids = content.split() return fids
每個領導都有一個fid用于區分,這里采用手動獲取fid并保存到txt中,在開始爬取時再逐行讀取,
5.獲取領導所有留言鏈接
def get_detail_urls(position, list_url): '''獲取每個領導的所有留言鏈接''' user_agent = get_user_agent() chrome_options.add_argument('user-agent=%s' % user_agent) drivertemp = webdriver.Chrome(options=chrome_options) drivertemp.maximize_window() drivertemp.get(list_url) time.sleep(2) ## 回圈加載頁面 while True: datestr = WebDriverWait(drivertemp, 10).until( lambda driver: driver.find_element_by_xpath( '//*[@id="list_content"]/li[position()=last()]/h3/span')).text.strip() datestr = re.search(r'\d{4}-\d{2}-\d{2}', datestr).group() date = dparser.parse(datestr, fuzzy=True) print('正在爬取鏈接 --', position, '--', date) if date < start_date: break ## 模擬點擊加載 try: WebDriverWait(drivertemp, 50, 2).until(EC.element_to_be_clickable((By.ID, "show_more"))) drivertemp.execute_script('window.scrollTo(document.body.scrollHeight, document.body.scrollHeight - 600)') time.sleep(get_time()) drivertemp.execute_script('window.scrollTo(document.body.scrollHeight - 600, document.body.scrollHeight)') WebDriverWait(drivertemp, 50, 2).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="show_more"]'))) drivertemp.find_element_by_xpath('//*[@id="show_more"]').click() except: break time.sleep(get_time() - 1) detail_elements = drivertemp.find_elements_by_xpath('//*[@id="list_content"]/li/h2/b/a') ## 獲取所有鏈接 for element in detail_elements: detail_url = element.get_attribute('href') yield detail_url drivertemp.quit()
根據第4步提供的fid找到一個領導對應的所有留言的鏈接,由于領導的留言串列并未一次顯示完,下方有一個加載更多按鈕,如下
每次需要進行點擊向下加載,所以要模擬點擊的操作,向下滑動,等完全加載后再次點擊,直到底部,函式回傳值時,不是一次回傳一個串列,而是通過yield關鍵字生成生成器,按照程式執行的進度生成url,可以減少記憶體的壓力,
6.獲取留言詳情
def get_message_detail(driver, detail_url, writer, position): '''獲取留言詳情''' print('正在爬取留言 --', position, '--', detail_url) driver.get(detail_url) ## 判斷,如果沒有評論則跳過 try: satis_degree = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_class_name("sec-score_firstspan")).text.strip() except: return ## 獲取留言各部分內容 message_date_temp = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_xpath("/html/body/div[6]/h3/span")).text message_date = re.search(r'\d{4}-\d{2}-\d{2}', message_date_temp).group() message_datetime = dparser.parse(message_date, fuzzy=True) if message_datetime < start_date: return message_title = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_class_name("context-title-text")).text.strip() label_elements = WebDriverWait(driver, 2.5).until(lambda driver: driver.find_elements_by_class_name("domainType")) try: label1 = label_elements[0].text.strip() label2 = label_elements[1].text.strip() except: label1 = '' label2 = label_elements[0].text.strip() message_content = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_xpath("/html/body/div[6]/p")).text.strip() replier = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_xpath("/html/body/div[8]/ul/li[1]/h3[1]/i")).text.strip() reply_content = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_xpath("/html/body/div[8]/ul/li[1]/p")).text.strip() reply_date_temp = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_xpath("/html/body/div[8]/ul/li[1]/h3[2]/em")).text reply_date = re.search(r'\d{4}-\d{2}-\d{2}', reply_date_temp).group() review_scores = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_elements_by_xpath("/html/body/div[8]/ul/li[2]/h4[1]/span/span/span")) resolve_degree = review_scores[0].text.strip()[:-1] handle_atti = review_scores[1].text.strip()[:-1] handle_speed = review_scores[2].text.strip()[:-1] review_content = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_xpath("/html/body/div[8]/ul/li[2]/p")).text.strip() is_auto_review = '是' if (('自動默認好評' in review_content) or ('默認評價' in review_content)) else '否' review_date_temp = WebDriverWait(driver, 2.5).until( lambda driver: driver.find_element_by_xpath("/html/body/div[8]/ul/li[2]/h4[2]/em")).text review_date = re.search(r'\d{4}-\d{2}-\d{2}', review_date_temp).group() ## 存入CSV檔案 writer.writerow( [position, message_title, label1, label2, message_date, message_content, replier, reply_content, reply_date, satis_degree, resolve_degree, handle_atti, handle_speed, is_auto_review, review_content, review_date])
我們只需要有評論的留言,因此在最開始要過濾掉沒有評論的留言,然后通過xpath、class_name等方式定位到相應的元素獲取留言的各個部分的內容,每條留言共保存14個內容,并保存到csv中,
7.獲取并保存領導所有留言
def get_officer_messages(index, fid): '''獲取并保存領導的所有留言''' user_agent = get_user_agent() chrome_options.add_argument('user-agent=%s' % user_agent) driver = webdriver.Chrome(options=chrome_options) list_url = "http://liuyan.people.com.cn/threads/list?fid={}##state=4".format(fid) driver.get(list_url) position = WebDriverWait(driver, 10).until( lambda driver: driver.find_element_by_xpath("/html/body/div[4]/i")).text ## time.sleep(get_time()) print(index, '-- 正在爬取 --', position) start_time = time.time() ## encoding='gb18030' csv_name = position + '.csv' ## 檔案存在則洗掉重新創建 if os.path.exists(csv_name): os.remove(csv_name) with open(csv_name, 'a+', newline='', encoding='gb18030') as f: writer = csv.writer(f, dialect="excel") writer.writerow( ['職位姓名', '留言標題', '留言標簽1', '留言標簽2', '留言日期', '留言內容', '回復人', '回復內容', '回復日期', '滿意程度', '解決程度分', '辦理態度分', '辦理速度分', '是否自動好評', '評價內容', '評價日期']) for detail_url in get_detail_urls(position, list_url): get_message_detail(driver, detail_url, writer, position) time.sleep(get_time()) end_time = time.time() crawl_time = int(end_time - start_time) crawl_minute = crawl_time // 60 crawl_second = crawl_time % 60 print(position, '已爬取結束!!!') print('該領導用時:{}分鐘{}秒,'.format(crawl_minute, crawl_second)) driver.quit() time.sleep(5)
獲取該領導的職位資訊并為該領導創建一個獨立的csv用于保存提取到的留言資訊,呼叫get_message_detail()方法獲取每條留言的具體資訊并保存,計算出每個領導的執行時間,
8.合并檔案
def merge_csv(): '''將所有檔案合并''' file_list = os.listdir('.') csv_list = [] for file in file_list: if file.endswith('.csv'): csv_list.append(file) ## 檔案存在則洗掉重新創建 if os.path.exists('DATA.csv'): os.remove('DATA.csv') with open('DATA.csv', 'a+', newline='', encoding='gb18030') as f: writer = csv.writer(f, dialect="excel") writer.writerow( ['職位姓名', '留言標題', '留言標簽1', '留言標簽2', '留言日期', '留言內容', '回復人', '回復內容', '回復日期', '滿意程度', '解決程度分', '辦理態度分', '辦理速度分', '是否自動好評', '評價內容', '評價日期']) for csv_file in csv_list: with open(csv_file, 'r', encoding='gb18030') as csv_f: reader = csv.reader(csv_f) line_count = 0 for line in reader: line_count += 1 if line_count != 1: writer.writerow( (line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7], line[8], line[9], line[10], line[11], line[12], line[13], line[14], line[15]))
將爬取的所有領導的資料進行合并,
9.主函式呼叫
def main(): '''主函式''' fids = get_fid() print('爬蟲程式開始執行:') s_time = time.time() for index, fid in enumerate(fids): try: get_officer_messages(index + 1, fid) except: get_officer_messages(index + 1, fid) print('爬蟲程式執行結束!!!') print('開始合成檔案:') merge_csv() print('檔案合成結束!!!') e_time = time.time() c_time = int(e_time - s_time) c_minute = c_time // 60 c_second = c_time % 60 print('{}位領導共計用時:{}分鐘{}秒,'.format(len(fids), c_minute, c_second)) if __name__ == '__main__': '''執行主函式''' main()
主函式中先獲取領導所有留言,再合并所有資料檔案,完成整個爬取程序,并統計整個程式的運行時間,便于分析運行效率,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/121096.html
標籤:其他
上一篇:求幫
