请教两个关于使用 python 爬去哪儿,携程等机票网站的问题 - V2EX
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 15632 次点击
    这是一个创建于 3416 天前的主题,其中的信息可能已经有所发展或是发生改变。

    初学 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', unaerSpider.__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  
    PRO
       2016-07-24 22:52:58 +08:00 via iPad   1
    公司业务的原因,我建议你别研究了。
    去哪得二三十人在研究爬虫和反爬虫。
    给你 islimit 这算给面子了,告诉你,小伙子要知难而退。
    去哪反扒的大招是接口正常返回,但数据都是错的。。。
    xxwar
        2
    xxwar  
       2016-07-24 22:58:45 +08:00
    楼上真是大招,无解
    GlobalNPC
        3
    GlobalNPC  
       2016-07-24 23:04:58 +08:00 via iPhone
    携程有白名单,谢谢。
    v2gba
        4
    v2gba  
       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  
    PRO
       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     Solana     2633 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 14:47 PVG 22:47 LAX 06:47 JFK 09:47
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86