python-携程爬虫

前言

这篇内容讲解js生成内容型,上一篇讲解的所需内容是全部在html源码中可以找得到的,而比如携程网随便打开一个旅店的页面,像图1酒店点评下面的位置等信息在开发者工具中的html代码中可看到在类为bar_score的div中,可是查看源代码搜索bar_score却并没有搜索结果,原因是因为这部分内容是由js代码生成,然后在页面渲染时填充进div中,故源代码中是找不到的。

python-携程爬虫_第1张图片
图1

python-携程爬虫_第2张图片

解决方式:Seleunium+BeautifulSoup

1.Selenium介绍####

Selenium本来是作为一个自动化测试工具。它可以模拟浏览器行为,比如打开浏览器,点击某个元素等等。所以可以利用它获取页面加载后的元素
安装
大多数的python第三方库都可以在以下网站中搜索找到,[python packageIndex]。后面提到的第三方库均可在该网站中下载。(https://pypi.python.org/)
Selenuim仅支持部分浏览器,这里以火狐浏览器为例,并且需要最好下载配套的Selenium和fiirefox,否则可能会遇到版本不兼容问题。这里所用到的是selenium2.42.1和firefox27.0.1(好老)。
操作
打开一个浏览器和网址

 from selenium import webdriver  

 driver = webdriver.Firefox() #打开火狐浏览器
 url = 'https://www.baidu.com'
 driver.set_page_load_timeout(20) #设置页面超时时间
 driver.get(url) #打开对应网址

常用方式定位元素

python-携程爬虫_第3张图片

等待
Selenium中的等待,有时候页面加载需要一定时间,如果立即定位元素可能会出现获取不到元素的错误,所以需要设置等待。

1.强制等待sleep(xx),强制让闪电侠等xx时间,不管浏览器能不能跟上速度,还是已经提前到了,都必须等xx秒。

sleep(3)

2.隐形等待是设置了一个最长等待时间,如果在规定时间内网页加载完成,则执行下一步,否则一直等到时间截止,然后执行下一步。隐性等待,最长等30秒

driver.implicitly_wait(30)

3.显性等待,WebDriverWait,配合该类的until()和until_not()方法,就能够根据判断条件而进行灵活地等待了。它主要的意思就是:程序每隔xx秒看眼,如果条件成立了,则执行下一步,否则继续等待,直到超过设置的最长时间,然后抛出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文件中提取数据的Python库,Beautiful Soup 3 目前已经停止开发,推荐在现在的项目中使用Beautiful Soup 4,不过它已经被移植到BS4了,也就是说导入时我们需要 import bs4 。

 from bs4 import BeautifulSoup

可以通过id、css类、文档树等多种方式提取数据,具体使用查看官方文档: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) #新建一个工作表,并设置可被改写
sheet1.col(0).width=256*22 #设置第一列的列宽,xlwt的列宽是以256为一个单位
style = xlwt.easyxf('align: wrap on, vert centre, horiz center') #easyxf设置表格样式,此处设置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) # 获取第四行内容
cols = sheet2.col_values(2) # 获取第三列内容

#单元格操作
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进行追加修改操作
追加方法如下,通过对原有文件建立一个临时副本,在副本后末尾写入新数据,再将原有文件覆盖

    rb_file = xlrd.open_workbook('D:/score.xls')
    nrows = rb_file.sheets()[0].nrows #获取已存在的excel表格行数
    wb_file = copy(rb_file) #复制该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])):
                #将数据追加至已有行后
                sheet1.write(i+nrows,j,score_list[i][j])
    except Exception, e:
        print e   
    wb_file.save('D:/score.xls') 

携程爬虫实现##

首先进入酒店列表的页面,此处是在首页设置城市为武汉之后进来的页面,在酒店列表获取红圈中的如下信息,查看详情是获取该按钮对应的链接地址,方便后面进入酒店的详情页面

python-携程爬虫_第4张图片
图一

然后是酒店详情页面的如下信息

python-携程爬虫_第5张图片
图二

最终获取数据结果如下图

程序的整体思路是,
1.以每一页为一个单位,爬取一页数据并存储完毕后,使用selenium模拟点击下一页。
2.每一页使用selenuim获取酒店列表的单条酒店信息的整体html,转换成beautifulsoup所需格式存储至hotel_list。
3.遍历hotel_list,使用beautifulsoup提取图一的信息,再跳转至详情页面爬取图二中的信息。将图一图二信息整合至hotel_info的元组,最后追加至info_list列表,构成一页所有酒店的所需信息。
4.每爬取一页存储至excel。因为使用selenium不断打开浏览器操作多了之后会出现浏览器无响应的情况,为避免最后没有数据,所有采用爬取一页存一页的方式。
代码简单讲解:

需下载的库,bs4,xlwt,xlrd,xlutils,selenium
程序结构main 为程序入口,bulid_excel建立excel文件供存储数据,read_hotel爬取数据,并设置每一页爬取完毕后调用save_score向建立的excel追加写入数据。

# -*- coding: utf-8 -*-  #这行很重要,所有python代码最好都以utf-8形式编码,以防止中文乱码
import urllib2 #和urlib类似,但是可以构造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()中的数字可指定需要爬取的页数
            for i in range(4):
                hotel_loc = []
                #获取该页的酒店列表
                hotel_loc = driver.find_elements_by_xpath("//div[@class='searchresult_list searchresult_list2']")
                count = 0
                page_count = len(hotel_loc)
                print page_count
                while count < page_count:
                    try:
                        hotel = ''
                        #获取酒店列表的html,供beautiful转换后方便提取
                        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:
                    #获取每一条酒店的总体评分、用户推荐%数、查看详情的链接地址
                    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']
                    #构造酒店详情信息的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()
                        #程序执行到该处使用driver等待的方式并不能进入超时exception,也不能进入设定好的页面加载超时错误?????
                    except socket.error:
                        print 'commtab error'
                        time.sleep(10)
                        #driver.execute("acceptAlert") #此行一直出错,浏览器跳出警告框????无法执行任何有关driver 的操作
                        #continue
                        #driver.quit()
                    try:
                        time.sleep(3)
                        bar_score = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_xpath("//div[@class='bar_score']"))
                    except Exception, e:
                        print 'bbbbbbbbbbbb'
                        print e
                    #bar_score = driver.find_element_by_xpath("//div[@class='bar_score']")
                    #对获取内容进行具体的正则提取
                    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
                    #获取位置、设施、服务、卫生评分
                    bar_scores_ptn = re.compile(r'\d.\d') #提取字符串中的数字,格式类似于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
                    #将每个酒店的所有数据以元祖形式存储进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提示,并调用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

        #建立数据存储的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')
        #将数据追加至已建立的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表格行数
            wb_file = copy(rb_file) #复制该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])):
                        #将数据追加至已有行后
                        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()

你可能感兴趣的:(python-携程爬虫)