V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
mikulch
V2EX  ›  Python

请教两个关于使用 python 爬去哪儿,携程等机票网站的问题

  •  
  •   mikulch · 2016-07-24 22:32:47 +08:00 · 15008 次点击
    这是一个创建于 3045 天前的主题,其中的信息可能已经有所发展或是发生改变。

    初学 python 。

    近期公司由于业务原因,需要想办法获取到携程与去哪儿的机票信息。 于是我尝试用 python+urllib 对这两个网站上的信息进行抓取。

    去哪儿的爬虫代码如下:(初学 python 。代码有很多不合理之处。望海涵。)

    # -*- coding:utf-8 -*-
    
    import urllib
    from urllib import request
    
    
    class QunaerSpider:
        __query_flights_base_url = 'http://flight.qunar.com/twelli/longwell?'
    
        __user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103'
    
        __referer_base_url = 'http://flight.qunar.com/site/interroundtrip_compare.htm?'
    
        __referer_end_url = '&from=fi_re_search&lowestPrice=null&isInter=true&favoriteKey=&showTotalPr=null'
    
        def create_flights_query_url(self, from_city, from_date, to_city, to_date):
            """根据用户传参,返回组装好的机票请求用 String 格式的 url 数据
    
            Args:
                from_city: 出发城市
                from_date: 出发日期
                to_city: 抵达城市
                to_date: 抵达日期
    
            Returns:
                返回组装好的机票请求用 String 格式的 url 数据.
    
            Raise:
                None
    
            """
            from_city_encoded = urllib.request.quote(from_city.encode('utf-8'))
            from_date_encoded = urllib.request.quote(from_date)
            to_city_encoded = urllib.request.quote(to_city.encode('utf-8'))
            to_date_encoded = urllib.request.quote(to_date)
            url = QunaerSpider.__query_flights_base_url
            # 初始化参数对象
            parameter_dict = {}
            parameter_dict['from'] = 'qunarindex'
            parameter_dict['fromCity'] = from_city_encoded
            parameter_dict['fromDate'] = from_date_encoded
            parameter_dict['isInter'] = 'true'
            parameter_dict['prePay'] = 'true'
            parameter_dict['locale'] = 'zh'
            parameter_dict['mergeFlag'] = '0'
            parameter_dict['nextNDays'] = '0'
            parameter_dict['op'] = '1'
            parameter_dict['reset'] = 'true'
            parameter_dict['searchLangs'] = 'zh'
            parameter_dict['searchType'] = 'RoundTripFlight'
            parameter_dict['toCity'] = to_city_encoded
            parameter_dict['toDate'] = to_date_encoded
            parameter_dict['version'] = 'thunder'
            parameter_dict['http://www.travelco.com/searchArrivalAirport'] = to_city_encoded
            parameter_dict['http://www.travelco.com/searchDepartureAirport'] = from_city_encoded
            parameter_dict['http://www.travelco.com/searchDepartureTime'] = from_date_encoded
            parameter_dict['http://www.travelco.com/searchReturnTime'] = to_date_encoded
            # f+时间戳
            parameter_dict['xd'] = 'f1469358871776'
            parameter_dict['www'] = 'true'
            parameter_dict['wyf'] = '0P8HfQ5%2F%2FYA%2FWldSERAyfudSERU0dUd0ERPj%3D%3D%3D%3D%7C1441321882698'
            parameter_dict['departureCity'] = from_city_encoded
            parameter_dict['arrivalCity'] = to_city_encoded
            parameter_dict['departureDate'] = from_date_encoded
            parameter_dict['returnDate'] = to_date_encoded
            # token.加不加暂时看不出来什么影响
            parameter_dict['_token'] = '6455'
    
            # 拼装 query_rul
            for k, v in parameter_dict.items():
                url = url + '&' + k + '=' + v
    
            print('请求字符串为\n%s' % url)
            return url
    
        def create_referer_url(self, from_city, from_date, to_city, to_date):
    
            from_city_encoded = urllib.request.quote(from_city.encode('utf-8'))
            from_date_encoded = urllib.request.quote(from_date)
            to_city_encoded = urllib.request.quote(to_city.encode('utf-8'))
            to_date_encoded = urllib.request.quote(to_date)
            url = QunaerSpider.__referer_base_url
            # 初始化参数对象
            parameter_dict = {}
            parameter_dict['from'] = 'qunarindex'
            parameter_dict['fromCity'] = from_city_encoded
            parameter_dict['fromDate'] = from_date_encoded
            parameter_dict['toCity'] = to_city_encoded
            parameter_dict['toDate'] = to_date_encoded
            # TODO 暂时写死
            parameter_dict['fromCode'] = 'CTU'
            parameter_dict['toCode'] = 'TYO'
    
            # 拼装 query_rul
            for k, v in parameter_dict.items():
                url = url + '&' + k + '=' + v
    
            url += QunaerSpider.__referer_end_url
    
            print('Referer 为\n%s' % url)
    
            return url
    
        def query_flights(self, url, referer_url):
            """根据用户传参,返回组装好的机票请求用 String 格式的 url 数据
    
                   Args:
                       url: 机票接口
    
                   Returns:
                       封装好的机票数据
    
                   Raise:
                       None
    
            """
            req = request.Request(url)
            req.add_header('Host', 'flight.qunar.com')
            req.add_header('Accept', '*/*')
            req.add_header('User-Agent', QunaerSpider.__user_agent)
            req.add_header('Connection', 'keep-alive')
            req.add_header('Accept-Encoding', 'gzip, deflate, sdch')
            req.add_header('Content-Type', 'application/json')
            req.add_header('Accept-Language', 'zh-CN,zh;q=0.8')
            req.add_header('Referer', referer_url)
    
            with request.urlopen(req) as f:
                # 读取数据
                data = f.read()
                print(f.status, f.reason)
                print('Data:', data.decode('utf-8'))
    
    
    qunaerSpider = QunaerSpider()
    referer_url = qunaerSpider.create_referer_url('成都', '2016-08-20', '东京', '2016-09-11')
    url = qunaerSpider.create_flights_query_url('成都', '2016-08-20', '东京', '2016-09-11')
    qunaerSpider.query_flights(url, referer_url)
    
    

    去哪儿网遇到的问题

    爬虫返回的信息为

    200 OK Data: {isLimit:true}

    使用同样的 url ,通过浏览器却能正常访问。想知道原因以及修正办法。

    携程网遇到的问题

    携程网的爬虫代码还没开始写。不过大概分析了下,携程网是 post 请求。 并且每次请求时会验证“ SearchKey ”与“ TransNo ”。关于这两个参数,有有经验的同学知道是在哪里,通过什么方式获取到的吗?附上携程的请求参数列表。

    {
    	"FlightWay": "D",
    	"SegmentList": [{
    		"DCityCode": "CTU",
    		"ACityCode": "TYO",
    		"DCity": "Chengdu|成都(CTU)|28|CTU|480",
    		"ACity": "Tokyo|东京(TYO)|228|TYO|540",
    		"DepartDate": "2016-8-1"
    	}, {
    		"DCityCode": "TYO",
    		"ACityCode": "CTU",
    		"DCity": "Tokyo|东京(TYO)|228|TYO|480",
    		"ACity": "Chengdu|成都(CTU)|28|CTU|540",
    		"DepartDate": "2016-9-10"
    	}],
    	"TransferCityID": 0,
    	"Quantity": 1,
    	// 每次请求发生变化,携程会验证此 key
    	"TransNo": "5516072421000033701",
    	"SearchRandomKey": "",
    	"IsAsync": 1,
    	"RecommendedFlightSwitch": 1,
    	// 每次请求发生变化。携程会验证此 key
    	"SearchKey": "BEBFB6F8C0C56B8561A9B435AE822DF4D499B75C2FFA74D481318741A7F9537EFB59C5327342DE0D1A11D1E626A03C6C843FE6E311D4819F",
    	"MultiPriceUnitSwitch": 1,
    	"TransferCitySwitch": false,
    	"EngineScoreABTest": "B",
    	"AdjacentDateKey": "",
    	"SearchStrategySwitch": 1,
    	"MaxSearchCount": 3,
    	"TicketRemarkSwitch": 1,
    	"RowNum": "1500",
    	"TicketRemarkChannels": ["GDS-WS", "ZY-WS"],
    	"AddSearchLogOneByOne": true,
    	"TFAirlineQTE": "AA",
    	"IsWifiPackage": 0
    }
    
    

    谢谢各位。

    42 条回复    2016-08-06 00:02:42 +08:00
    sivacohan
        1
    sivacohan  
       2016-07-24 22:52:58 +08:00 via iPad   ❤️ 1
    公司业务的原因,我建议你别研究了。
    去哪得二三十人在研究爬虫和反爬虫。
    给你 islimit 这算给面子了,告诉你,小伙子要知难而退。
    去哪反扒的大招是接口正常返回,但数据都是错的。。。
    xxwar
        2
    xxwar  
       2016-07-24 22:58:45 +08:00
    楼上真是大招,无解
    infun
        3
    infun  
       2016-07-24 23:04:58 +08:00 via iPhone
    携程有白名单,谢谢。
    MrGba2z
        4
    MrGba2z  
       2016-07-24 23:21:28 +08:00
    大招有点刺激啊
    linuxchild
        5
    linuxchild  
       2016-07-24 23:32:36 +08:00 via Android
    长见识啦
    mikulch
        6
    mikulch  
    OP
       2016-07-25 00:28:32 +08:00
    最神奇的是这两家网站,国际航班和国内航班的爬取难度完全不同
    国内的航班很轻松就爬到信息了。
    但是国际航班各种反爬手段。。。。。
    Jolly23
        7
    Jolly23  
       2016-07-25 02:41:55 +08:00 via iPhone
    爬到的数据全是假的,人家玩你
    20150517
        8
    20150517  
       2016-07-25 03:24:14 +08:00 via Android
    我认识一个人,爬了携程去哪儿大量机票信息,应该有上 gb 数据,你公司就说出钱来买
    wojiaodaxiaxia
        9
    wojiaodaxiaxia  
       2016-07-25 03:58:05 +08:00
    http://www.v4.cc/News-1749172.html
    这个视频有点意思,原链接找不到,带视频的只有这个,将就看下咯
    wojiaodaxiaxia
        10
    wojiaodaxiaxia  
       2016-07-25 04:04:35 +08:00
    @mikulch
    上面那条是我的锅,花五毛补上
    原链接:https://segmentfault.com/a/1190000005840672
    视频:http://v.qq.com/x/page/j0308hykvot.html
    googlebot
        11
    googlebot  
       2016-07-25 05:42:20 +08:00 via Android
    qunar 数据在实时变化,根本没爬的意义
    redhatping
        12
    redhatping  
       2016-07-25 08:11:19 +08:00 via iPhone
    人家问代码的
    rale
        13
    rale  
       2016-07-25 08:31:28 +08:00
    要爬机票的信息去各大航空公司官网,这才是源头, qunar , ctrip 只是相当于一个中介而已
    holajamc
        14
    holajamc  
       2016-07-25 08:58:30 +08:00
    Selenium + PhantomJS 正儿八经开个浏览器~所以觉得楼上的问题应该不存在了~
    manoon
        15
    manoon  
       2016-07-25 09:07:06 +08:00 via iPhone
    @rale 应该是有一家叫中航信的公司

    他们的数据 /接口都是在这家公司买的吧

    2010 年的时候,我也尝试爬过。最后源头都指向了这家公司好像。
    jugelizi
        16
    jugelizi  
       2016-07-25 10:25:14 +08:00   ❤️ 1
    "初学 python 。

    近期公司由于业务原因"

    我就笑了
    小伙子掉坑里了
    murmur
        17
    murmur  
       2016-07-25 10:32:56 +08:00
    @20150517 没任何意义啊 机票数据分分钟变 这又不是航班表。。
    est
        18
    est  
       2016-07-25 10:34:41 +08:00
    唉。卧槽。协程和去哪儿其实他们本来就是爬别人的。就是自动化了航空公司的自动下单选座系统。
    mornlight
        19
    mornlight  
       2016-07-25 10:41:28 +08:00   ❤️ 1
    如果是为了学习,建议换别的站点,如果是业务需要,走别的路子。
    这两家的反爬虫策略很恶心, PhantomJS 也没用。你以为你拿到了一堆数据,结果是真假数据混合在一起,偶尔还被携程的人嘲讽。
    rale
        20
    rale  
       2016-07-25 10:48:26 +08:00
    @manoon 嗯,对,航信的接口收费的,那些航空公司的网站都做的比较破,免费且好抓
    holajamc
        21
    holajamc  
       2016-07-25 11:01:22 +08:00
    @mornlight Selenium 和 PJS 搭配也不行咩~ 就是打开浏览器去拿渲染出来的值~我想应该没问题吧
    glogo
        22
    glogo  
       2016-07-25 12:16:56 +08:00
    @sivacohan 哇呜,可否详细一下设计细节,如何识别爬虫和投毒的
    Weixiao0725
        23
    Weixiao0725  
       2016-07-25 12:57:10 +08:00
    @manoon 正解,我一个同学就在中航信上班,他就是写接口给第三方调用获取航班信息的
    manoon
        24
    manoon  
       2016-07-25 12:59:59 +08:00
    @Weixiao0725 哈哈,说明我虽然老了,但记忆力是不错的。
    krizex
        25
    krizex  
       2016-07-25 13:17:24 +08:00
    https://github.com/krizex/ATP
    我去年用 py 写的抓 qunar 上国内航班的数据,然而并没有觉得返回给我的数据是假的。
    gimp
        26
    gimp  
       2016-07-25 13:32:00 +08:00
    防爬有一个阈值的,当爬虫和真实用户没什么区别的时候,网站方是不敢塞给你假数据的。
    cszhiyue
        27
    cszhiyue  
       2016-07-25 13:55:12 +08:00
    爬虫不是所见所得吗?假数据这个怎么来。正常用户浏览也返回假数据吗?
    @Jolly23
    akring
        28
    akring  
       2016-07-25 14:00:41 +08:00
    @sivacohan 一颗赛艇
    shakespaces
        29
    shakespaces  
       2016-07-25 14:02:41 +08:00 via Android
    @cszhiyue 有反爬虫的,正常用户浏览肯定和爬虫获取有种种不同
    cszhiyue
        30
    cszhiyue  
       2016-07-25 14:07:06 +08:00
    @shakespaces 用户的浏览行为完全可以伪造。种种不同可不可以举例一下。
    shakespaces
        31
    shakespaces  
       2016-07-25 14:23:23 +08:00 via Android
    @cszhiyue 比如速度,你确实可以模拟用户几十秒几分钟获取一次,但是时间成本太高了吧。分布式又增加了服务器成本,不计成本确实能够完全模拟用户
    20150517
        32
    20150517  
       2016-07-25 14:31:17 +08:00 via Android
    @murmur 我意思是买 api 接口
    cszhiyue
        33
    cszhiyue  
       2016-07-25 14:34:32 +08:00
    @shakespaces 大概理解了。速度不加以限制的话,频率过高,服务不是拒绝,而是以返回假数据这种形式来欺骗爬虫? 速度的话可以通过切换代理 ip 的形式。爬虫还是不能完全杜绝,反爬是提高爬虫的成本
    KotiyaSanae
        34
    KotiyaSanae  
       2016-07-25 14:46:11 +08:00
    @mikulch 因为国际接口请求是收费的。而且不低。所以甄别是否是爬虫很重要。
    mikulch
        35
    mikulch  
    OP
       2016-07-25 14:47:09 +08:00
    @krizex 去哪儿的国内航班的数据倒是相对好抓一点,反爬措施和国际机票的不太一样。
    我现在直接用 postman 请求去哪儿的国际机票 postman 的参数和浏览器设置成完全一样,反不回来数据。
    但是用 postman 请求国内的机票就没问题。
    realpg
        36
    realpg  
       2016-07-25 14:53:05 +08:00
    老老实实去航信买数据就是了……
    killerv
        37
    killerv  
       2016-07-25 15:25:41 +08:00
    就像楼上说的,反爬并不能完全杜绝,只是提高抓取成本。
    krizex
        38
    krizex  
       2016-07-25 22:56:55 +08:00
    @mikulch 我当时的需求就是抓下国内的航班看看价格趋势好买机票。国际的倒是没搞过,不过既然是国际航班了,为什么不用 google 的数据呢。每天前 50 次查询免费,搞个几千个账号应该能满足你每天的查询次数了吧?
    https://developers.google.com/qpx-express/v1/prereqs
    krizex
        39
    krizex  
       2016-07-25 22:58:25 +08:00
    @mikulch 另外携程的数据可以购买的,你们既然是企业搞,出点钱也不是不行吧?
    nightspirit
        40
    nightspirit  
       2016-07-26 16:02:09 +08:00
    反爬虫趋势无法杜绝,如果设置的过滤条件过高,就会有误杀
    bdbai
        41
    bdbai  
       2016-08-01 19:03:03 +08:00 via Android
    携程的两个 key 在查询页面顶部有写,但 searchkey 被 js 处理过。
    bdbai
        42
    bdbai  
       2016-08-06 00:02:42 +08:00 via Android
    append: 携程抓手机版无压力
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   6002 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 03:17 · PVG 11:17 · LAX 19:17 · JFK 22:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.