python-携程爬虫

作者: 周一ing | 来源:发表于2017-10-13 16:07 被阅读0次

    前言

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

    图1

    解决方式: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) #打开对应网址
    

    常用方式定位元素


    等待
    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') 
    

    携程爬虫实现##

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

    图一

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

    图二

    最终获取数据结果如下图

    程序的整体思路是,
    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-携程爬虫

        本文链接:https://www.haomeiwen.com/subject/donwtxtx.html