python-携程爬虫

前言

这篇内容讲解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()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 声明:本文讲解的实战内容,均仅用于学习交流,请勿用于任何商业用途! 一、前言 强烈建议:请在电脑的陪同下,阅读本文...
    Bruce_Szh阅读 12,674评论 6 28
  • 学习爬虫有一段时间了,期间接触了很多相关的库,不禁感慨Python就是强大,当你遇到任何问题的时候基本上都有前人造...
    HomerX阅读 7,800评论 0 13
  • 转自链接 2.3.5 IF函数 2.3.6 CountIf和SumIf函数 2.3.7 Lookup函数 2.3....
    腿毛裤阅读 12,806评论 0 0
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,436评论 6 428
  • 自从9月1日接了这个班,我变着法儿教学生读书写字,一心想提高他们的学习成绩。可不知为什么,还是效果不佳!四个学生还...
    静致温婉阅读 168评论 1 1