python-攜程爬蟲

前言

這篇內(nèi)容講解js生成內(nèi)容型,上一篇講解的所需內(nèi)容是全部在html源碼中可以找得到的,而比如攜程網(wǎng)隨便打開一個旅店的頁面,像圖1酒店點評下面的位置等信息在開發(fā)者工具中的html代碼中可看到在類為bar_score的div中,可是查看源代碼搜索bar_score卻并沒有搜索結(jié)果,原因是因為這部分內(nèi)容是由js代碼生成,然后在頁面渲染時填充進div中,故源代碼中是找不到的。

圖1

解決方式:Seleunium+BeautifulSoup

1.Selenium介紹####

Selenium本來是作為一個自動化測試工具。它可以模擬瀏覽器行為,比如打開瀏覽器,點擊某個元素等等。所以可以利用它獲取頁面加載后的元素
安裝
大多數(shù)的python第三方庫都可以在以下網(wǎng)站中搜索找到,[python packageIndex]。后面提到的第三方庫均可在該網(wǎng)站中下載。(https://pypi.python.org/)
Selenuim僅支持部分瀏覽器,這里以火狐瀏覽器為例,并且需要最好下載配套的Selenium和fiirefox,否則可能會遇到版本不兼容問題。這里所用到的是selenium2.42.1和firefox27.0.1(好老)。
操作
打開一個瀏覽器和網(wǎng)址

 from selenium import webdriver  

 driver = webdriver.Firefox() #打開火狐瀏覽器
 url = 'https://www.baidu.com'
 driver.set_page_load_timeout(20) #設(shè)置頁面超時時間
 driver.get(url) #打開對應(yīng)網(wǎng)址

常用方式定位元素


等待
Selenium中的等待,有時候頁面加載需要一定時間,如果立即定位元素可能會出現(xiàn)獲取不到元素的錯誤,所以需要設(shè)置等待。

1.強制等待sleep(xx),強制讓閃電俠等xx時間,不管瀏覽器能不能跟上速度,還是已經(jīng)提前到了,都必須等xx秒。

sleep(3)

2.隱形等待是設(shè)置了一個最長等待時間,如果在規(guī)定時間內(nèi)網(wǎng)頁加載完成,則執(zhí)行下一步,否則一直等到時間截止,然后執(zhí)行下一步。隱性等待,最長等30秒

driver.implicitly_wait(30)

3.顯性等待,WebDriverWait,配合該類的until()和until_not()方法,就能夠根據(jù)判斷條件而進行靈活地等待了。它主要的意思就是:程序每隔xx秒看眼,如果條件成立了,則執(zhí)行下一步,否則繼續(xù)等待,直到超過設(shè)置的最長時間,然后拋出TimeoutException。隱性等待和顯性等待可以同時用,但要注意:等待的最長時間取兩者之中的大者

from selenium.webdriver.support.wait import WebDriverWait  

WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located(locator))

2.BeautifulSoup####

Beautiful Soup是一個可以從HTML或XML文件中提取數(shù)據(jù)的Python庫,Beautiful Soup 3 目前已經(jīng)停止開發(fā),推薦在現(xiàn)在的項目中使用Beautiful Soup 4,不過它已經(jīng)被移植到BS4了,也就是說導(dǎo)入時我們需要 import bs4 。

 from bs4 import BeautifulSoup

可以通過id、css類、文檔樹等多種方式提取數(shù)據(jù),具體使用查看官方文檔:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
舉例:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc,"html.parser")
comment_soup = index.findAll("div", { "class" : "v2_reviewsitem" })

3.xlwt、xlrd、xlutils進行excel讀寫操作

xlwt::對excel進行寫操作
基本操作如下,詳情可參考以下文檔:http://xlwt.readthedocs.io/en/latest/genindex.html
file = xlwt.Workbook() #新建一個工作簿
sheet1 = file.add_sheet(u'sheet1',cell_overwrite_ok=True) #新建一個工作表,并設(shè)置可被改寫
sheet1.col(0).width=256*22 #設(shè)置第一列的列寬,xlwt的列寬是以256為一個單位
style = xlwt.easyxf('align: wrap on, vert centre, horiz center') #easyxf設(shè)置表格樣式,此處設(shè)置align為垂直水平居中
sheet1.write(0,1,‘text',style) #向第1行第2列以style樣式寫入text字符串

xlrd:對excel進行讀操作

rb_file = xlrd.open_workbook('D:/score.xls') #打開文件
# 獲取指定sheet的方式
sheet1 = rb_file.get_sheet(0)
sheet2 = rb_file.sheet_by_index(1) # sheet索引從0開始
sheet3 = rb_file.sheet_by_name('sheet2')

# sheet的姓名、行列操作
print sheet2.name,sheet2.nrows,sheet2.ncols
rows = sheet2.row_values(3) # 獲取第四行內(nèi)容
cols = sheet2.col_values(2) # 獲取第三列內(nèi)容

#單元格操作
print sheet2.cell(1,0).value.encode('utf-8')
print sheet2.cell_value(1,0).encode('utf-8')
print sheet2.row(1)[0].value.encode('utf-8')
print sheet2.cell(1,0).ctype

xlutils:對excel進行追加修改操作
追加方法如下,通過對原有文件建立一個臨時副本,在副本后末尾寫入新數(shù)據(jù),再將原有文件覆蓋

    rb_file = xlrd.open_workbook('D:/score.xls')
    nrows = rb_file.sheets()[0].nrows #獲取已存在的excel表格行數(shù)
    wb_file = copy(rb_file) #復(fù)制該excel
    sheet1 = wb_file.get_sheet(0) #建立該excel的第一個sheet副本,通過sheet_by_index()獲取的sheet沒有write()方法
    try:
        for i in range(0,len(score_list)):
            for j in range(0,len(score_list[i])):
                #將數(shù)據(jù)追加至已有行后
                sheet1.write(i+nrows,j,score_list[i][j])
    except Exception, e:
        print e   
    wb_file.save('D:/score.xls') 

攜程爬蟲實現(xiàn)##

首先進入酒店列表的頁面,此處是在首頁設(shè)置城市為武漢之后進來的頁面,在酒店列表獲取紅圈中的如下信息,查看詳情是獲取該按鈕對應(yīng)的鏈接地址,方便后面進入酒店的詳情頁面

圖一

然后是酒店詳情頁面的如下信息

圖二

最終獲取數(shù)據(jù)結(jié)果如下圖

程序的整體思路是,
1.以每一頁為一個單位,爬取一頁數(shù)據(jù)并存儲完畢后,使用selenium模擬點擊下一頁。
2.每一頁使用selenuim獲取酒店列表的單條酒店信息的整體html,轉(zhuǎn)換成beautifulsoup所需格式存儲至hotel_list。
3.遍歷hotel_list,使用beautifulsoup提取圖一的信息,再跳轉(zhuǎn)至詳情頁面爬取圖二中的信息。將圖一圖二信息整合至hotel_info的元組,最后追加至info_list列表,構(gòu)成一頁所有酒店的所需信息。
4.每爬取一頁存儲至excel。因為使用selenium不斷打開瀏覽器操作多了之后會出現(xiàn)瀏覽器無響應(yīng)的情況,為避免最后沒有數(shù)據(jù),所有采用爬取一頁存一頁的方式。
代碼簡單講解:

需下載的庫,bs4,xlwt,xlrd,xlutils,selenium
程序結(jié)構(gòu)main 為程序入口,bulid_excel建立excel文件供存儲數(shù)據(jù),read_hotel爬取數(shù)據(jù),并設(shè)置每一頁爬取完畢后調(diào)用save_score向建立的excel追加寫入數(shù)據(jù)。

# -*- coding: utf-8 -*-  #這行很重要,所有python代碼最好都以utf-8形式編碼,以防止中文亂碼
import urllib2 #和urlib類似,但是可以構(gòu)造request請求
from bs4 import BeautifulSoup
import re
import os #文件操作庫
import xlwt
import xlrd 
from xlutils.copy import copy

from selenium import webdriver
from selenium.common.exceptions import TimeoutException

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time #時間操作庫,強制等待sleep需引入
import traceback #一個很好的錯誤追蹤
import socket

#driver.set_page_load_timeout(30)
socket.setdefaulttimeout(30)
class CommentContent:
        #讀取武漢酒店列表
        def read_hotel(self):
            driver = webdriver.Firefox() #打開瀏覽器
            driver.maximize_window() #最大化窗口
            page = 0 #提示作用
            index_url = 'http://hotels.ctrip.com/hotel/wuhan477#ctm_ref=ctr_hp_sb_lst'
            driver.get(index_url) #打開待爬取酒店列表頁面
            #模擬瀏覽器勾選酒店類型為青年旅社
            btn_qn = driver.find_element_by_id('feature-35')
            btn_qn.click()
            time.sleep(5)
            info_list = []
            hotel_list = [] #存儲所有帶爬取酒店的信息
            #range()中的數(shù)字可指定需要爬取的頁數(shù)
            for i in range(4):
                hotel_loc = []
                #獲取該頁的酒店列表
                hotel_loc = driver.find_elements_by_xpath("http://div[@class='searchresult_list searchresult_list2']")
                count = 0
                page_count = len(hotel_loc)
                print page_count
                while count < page_count:
                    try:
                        hotel = ''
                        #獲取酒店列表的html,供beautiful轉(zhuǎn)換后方便提取
                        hotel = hotel_loc[count].get_attribute("innerHTML")
                        hotel_list.append(BeautifulSoup(hotel,"html.parser"))
                        count += 1
                    except Exception,e:
                        #print e
                        print 'get hotel html error'
                        #continue
                #點擊下一頁按鈕
                btn_next = driver.find_element_by_id('downHerf')
                btn_next.click()
                time.sleep(10)

            count = 0
            hotel_count = len(hotel_list)
            #遍歷酒店列表里的每一條酒店信息
            while count < hotel_count:
                try:
                    #獲取每一條酒店的總體評分、用戶推薦%數(shù)、查看詳情的鏈接地址
                    hotel_name  = hotel_list[count].findAll("h2",{"class","searchresult_name"})[0].contents[0]['title']
                    total_judgement_score = hotel_list[count].findAll("span", { "class" : "total_judgement_score" })[1].get_text()            
                    hotel_judgement = hotel_list[count].find("span", { "class" : "hotel_judgement" }).get_text()
                    detail_href = hotel_list[count].find("a", { "class" : "btn_buy" })['href']
                    #構(gòu)造酒店詳情信息的url
                    detail_url = 'http://hotels.ctrip.com/' + detail_href
                    try:
                        #進入酒店詳情頁面
                        print '1-------------'
                        driver.get(detail_url)
                        print "start new page"
                    except TimeoutException:  
                        print 'time out after 30 seconds when loading page'  
                    time.sleep(3)
                    #點擊酒店點評
                    try:                    
                        WebDriverWait(driver, 5).until(lambda x: x.find_element_by_id("commentTab")).click()
                        #程序執(zhí)行到該處使用driver等待的方式并不能進入超時exception,也不能進入設(shè)定好的頁面加載超時錯誤?????
                    except socket.error:
                        print 'commtab error'
                        time.sleep(10)
                        #driver.execute("acceptAlert") #此行一直出錯,瀏覽器跳出警告框????無法執(zhí)行任何有關(guān)driver 的操作
                        #continue
                        #driver.quit()
                    try:
                        time.sleep(3)
                        bar_score = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_xpath("http://div[@class='bar_score']"))
                    except Exception, e:
                        print 'bbbbbbbbbbbb'
                        print e
                    #bar_score = driver.find_element_by_xpath("http://div[@class='bar_score']")
                    #對獲取內(nèi)容進行具體的正則提取
                    total_score_ptn = re.compile('(.*?)%')
                    try:
                        total_score = total_score_ptn.findall(total_judgement_score)[0]
                        #total_score = total_score_ptn.search(total_judgement_score).group(1)
                        hotel_sale_ptn = re.compile(r'\d+')
                        #hotel_sale = hotel_sale_ptnsearch(hotel_judgement).group(1)
                        hotel_sale = hotel_sale_ptn.findall(hotel_judgement)[0]
                    except Exception, e:
                        print 'tote error'
                        print e
                    #獲取位置、設(shè)施、服務(wù)、衛(wèi)生評分
                    bar_scores_ptn = re.compile(r'\d.\d') #提取字符串中的數(shù)字,格式類似于3.4
                    bar_scores = bar_scores_ptn.findall(bar_score.text)
                    try:
                        loc_score = bar_scores[0]
                        device_score = bar_scores[1]
                        service_score = bar_scores[2]
                        clean_score = bar_scores[3]
                    except Exception, e:
                        print '0------'
                        print e
                        continue
                    #將每個酒店的所有數(shù)據(jù)以元祖形式存儲進hotel_info,存儲成元組是為了方便后面寫入excel,
                    #后將所有酒店信息追加至info_list
                    hotel_info = (hotel_name,total_score,hotel_sale,loc_score,device_score,service_score,clean_score)
                    info_list.append(hotel_info)
                    count += 1
                    #每一頁有25個酒店,每爬取一頁顯示next page提示,并調(diào)用save_score方法存儲進excel.
                    #另外重啟瀏覽器,以防止其崩潰
                    if count % 24 == 0:
                        print "next page"
                        CommentContent().save_score(info_list)
                        info_list = []
                        driver.close()
                        time.sleep(10)
                        driver = webdriver.Firefox()

                except Exception, e:
                    print 'get detail info error'
                    #print e
                    count += 1
                    continue
                    #traceback.print_exc()
                    #driver.close()
                    break                
            return info_list

        #建立數(shù)據(jù)存儲的excel和格式,以及寫入第一行
        def build_excel(self):
            file = xlwt.Workbook()
            sheet1 = file.add_sheet(u'sheet1',cell_overwrite_ok=True)
            head_style = xlwt.easyxf('font: name Times New Roman, color-index red, bold on',
        num_format_str='#,##0.00')
            row0 = ('hotel_name','total_score','sale','loc_score','device_score', 'service_score', 'clean_score')
            for i in range(0,len(row0)):
                sheet1.write(0,i,row0[i],head_style)
            file.save('D:/score1.xls')
        #將數(shù)據(jù)追加至已建立的excel文件中
        def save_score(self, info_list):
            score_list = info_list
            rb_file = xlrd.open_workbook('D:/score.xls')
            nrows = rb_file.sheets()[0].nrows #獲取已存在的excel表格行數(shù)
            wb_file = copy(rb_file) #復(fù)制該excel
            sheet1 = wb_file.get_sheet(0) #建立該excel的第一個sheet副本
            try:
                for i in range(0,len(score_list)):
                    for j in range(0,len(score_list[i])):
                        #將數(shù)據(jù)追加至已有行后
                        sheet1.write(i+nrows,j,score_list[i][j])
            except Exception, e:
                print e                  
            wb_file.save('D:/score1.xls') 
            print  'save success'    
    #程序入口
    if __name__ == '__main__':
        CommentContent().build_excel()
        CommentContent().read_hotel()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內(nèi)容

  • 聲明:本文講解的實戰(zhàn)內(nèi)容,均僅用于學(xué)習(xí)交流,請勿用于任何商業(yè)用途! 一、前言 強烈建議:請在電腦的陪同下,閱讀本文...
    Bruce_Szh閱讀 12,759評論 6 28
  • 學(xué)習(xí)爬蟲有一段時間了,期間接觸了很多相關(guān)的庫,不禁感慨Python就是強大,當(dāng)你遇到任何問題的時候基本上都有前人造...
    HomerX閱讀 7,824評論 0 13
  • 轉(zhuǎn)自鏈接 2.3.5 IF函數(shù) 2.3.6 CountIf和SumIf函數(shù) 2.3.7 Lookup函數(shù) 2.3....
    腿毛褲閱讀 12,926評論 0 0
  • # Python 資源大全中文版 我想很多程序員應(yīng)該記得 GitHub 上有一個 Awesome - XXX 系列...
    aimaile閱讀 26,537評論 6 427
  • 自從9月1日接了這個班,我變著法兒教學(xué)生讀書寫字,一心想提高他們的學(xué)習(xí)成績。可不知為什么,還是效果不佳!四個學(xué)生還...
    靜致溫婉閱讀 172評論 1 1