爬取航班数据

针对飞常准航旅纵横,爬取航段+航班日期航班号+航班日期数据

url

飞常准:

航段
'http://webapp.veryzhun.com/h5/flightsearch?arr=%s&dep=%s&' \
'date=%s&token=根据自己的浏览器来' % (arr, dep, fData)
航班
'http://webapp.veryzhun.com/h5/flightsearch?fnum=%s&date=%s' \
'&token=小仙女的马赛克' % (fNo, fData)
arr:目的地 dep:出发地 fData:航班日期 fNo:航班号

航旅纵横:

航段
'http://www.umetrip.com/mskyweb/fs/fa.do?dep=' \
'%s&arr=%s&date=%s&channel=' % (arr, dep, fData)
航班
'http://www.umetrip.com/mskyweb/fs/fc.do?' \
'flightNo=%s&date=%s&channel=' % (fNo, fData)

爬取数据

飞常准的数据格式是json,返回一个数组,里面包含了航班的所有信息。 如果想知道某机场的所有航班信息情况,那么只用按照航段+航班日期来爬取就行了。(异常信息可以根据自己遇到的情况添加)

try:
    html = urlopen(url).read().strip().decode("utf-8")
    return json.loads(html)
except (HTTPError,URLError,AttributeError,
    requests.exceptions.ReadTimeout,
    requests.exceptions.ConnectionError,
    json.decoder.JSONDecodeError) as e:
    return ""

航旅纵横是html格式的,与飞常准不同的是,获取某机场航班所有信息,只能先爬取航段数据,得到该航段的所有航班号,再根据航班号查询。 (异常同上 这里简写)

html = urlopen(url)
bsObj = BeautifulSoup(html.read())  

数据解析

飞常准是标准的 json 格式,把数据直接入库就可以。

航旅纵横的航班数据是用javascript渲染的,我用了比较不灵活的办法(用字符串分隔)。

接着对航班数据再进行一次 BeautifulSoup 处理,就可以得到span里的数据啦

复杂的这块代码就不粘贴了

验证码问题

飞常准爬取次数>10 的时候,会出现请输入验证码

第一次的验证码就是纯图片,我机智的模拟输入111,–验证码有误 请重新输入。这次的验证码就正常了。

下载验证码

首先得把验证码下载下来,才能识别不是。。

1
2
3
4
5
6
try:
f = open("VerificationCode.jpg", 'wb')
f.write(urlopen(picUrl).read())
f.close()
except Exception as e:
print(picUrl+" error")
识别验证码

因为这个验证码比较简单,我就用pytesseract来识别了。

验证码是数字+字母的组合,但有时候会识别到特殊符号导致出错,我就用正则表达式过滤掉了。

1
2
3
image = Image.open("VerificationCode.jpg")
yzm = pytesseract.image_to_string(image)
yzm = re.sub('[^a-zA-Z0-9]',"",yzm)

封ip问题

飞常准没爬几次验证码就出现了,之后再爬几次就封ip了。具体表现为:操作频繁

航旅纵横就比较友好了,我能爬很久,但就是数据不全。。我想要的它没有,不过航旅纵横的app里数据很全,厉害的人可以试着去爬app



那么问题出现了,接下来代理ip上场。

代理ip

我试过 讯代理全网代理IP

这两个都还算稳定的,选择哪个主要还是看自己需求,挑个便宜的

讯代理的动态转发

官方提供的代码很简单,就是把headers封装成他们的服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def getAuth():
timestamp = str(int(time.time())) # 计算时间戳
string = "orderno=" + orderno + "," + "secret=" + secret + "," + "timestamp=" + timestamp
if is_python3:
string = string.encode()
sign = hashlib.md5(string).hexdigest().upper()
auth = "sign=" + sign + "&" + "orderno=" + orderno + "&" + "timestamp=" + timestamp
return auth
def getXundailiIp():
ip_port = ip + ":" + port
auth = getAuth()
proxy = {"http": "http://" + ip_port, "https": "https://" + ip_port}
headers = {"Proxy-Authorization": auth}
return headers,proxy

下了一个1块钱10000次订单。用起来不是特别稳定,不过影响不大。

全网代理IP

我买了4小时的动态代理IP,每次接口返回一个IP,IP有效期1分钟。用起来也还不错。有时候响应的比较慢,我在请求的时候加了超时时间。

这套代码就更简单啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def getProxyip():
# 这里填写全网代理IP提供的API订单号(请到用户中心获取)
order = "小仙女的马赛克"
# 获取IP的API接口
apiUrl = "http://dynamic.goubanjia.com/dynamic/get/" + order + ".html"
try:
# 获取IP列表
res = urlopen(apiUrl).read().strip()
# 按照\n分割获取到的IP
# ips = res.split("\n")
# 随机选择一个IP
# proxyip = random.choice(ips)
# 使用代理IP请求目标网址
proxyip = res.decode(encoding="utf-8")
return proxyip
except Exception:
return ""

因为我每次返回只有一个ip啦,不需要切割再随机选择一个

代码

已上传到github https://github.com/pangrou/machine-learning/tree/master/scrape