思路:查看网页源码可以看出,289个页面按钮的的URL的规律很明显
![页码按钮的链接](http://ww1.sinaimg.cn/large/006jjZD4ly1g68gx15dsej31hc0u0x4n.jpg)
可以将这些URL写入一个test.txt文件。对于每一页面根据HTML可以看出,找到table标签下的table标签,该table标签下的所有a标签就是机场详细信息的链接
![页面上机场详细信息的链接](http://ww1.sinaimg.cn/large/006jjZD4ly1g68h8vookdj31hc0u01kx.jpg)
进入机场的详细信息页面,查看源码,发现规律也很强,在table的table下的tbody有机场的详细信息。tr下的两个td有对应信息的属性和值,很容易取出。
![机场详细信息页面](http://ww1.sinaimg.cn/large/006jjZD4ly1g68hgpo644j31hc0u0kjb.jpg)
总体思路就是:将所有页码链接的URL存入test.txt文件,每次读取一个页面的URL(一行)访问,将该页面上的机场链接提取出来存入列表,分别访问列表中的机场详细信息URL,从中提取出机场详细信息
由于某些机场的详细信息页面源码提供的信息并不正确,需要执行js脚本后才能得到正确的信息。因此选择selenium+phantomjs组合爬取页面(selenium是一个测试工具,phantomjs是一个无界面浏览器,selenium通过打开phantomjs.exe加载phantomjs浏览器),可以获取加载js后的HTML页面
代码如下
import requests # 发请求的库
from bs4 import BeautifulSoup # 解析HTML的库
import json
import time
from selenium import webdriver # 加载浏览器的库
airportInfoList = [] # 保存所有机场详细信息的列表,元素是字典
siteURL = "https://airportcode.51240.com" # 网站资源URL的共同前缀
def getAirportInfo(PageURL, page): # 获取每个页面的机场列表
try: # 设置异常处理可以在爬取异常时,将已经爬取的内容写入文件
browser = webdriver.PhantomJS(executable_path=r'C:\phantomjs\bin\phantomjs') # 加载浏览器
response = requests.get(PageURL) # requests请求网页,这个只是获取机场的URL列表,不需要加载js,直接requests请求
response.raise_for_status() # 出错抛异常
response.encoding = response.apparent_encoding
html = response.text
soup = BeautifulSoup(html, 'html.parser') # 解析html文件
tag_as = soup.table.table.find_all('a')
length = str(len(tag_as))
i = 1
for tag_a in tag_as:
airportDetailURL = siteURL + tag_a.attrs['href']
print("第:" + str(page) + "/298 个页面的第:" + str(i) + "/" + length + "个机场")
getAirportDetailInfo(airportDetailURL, browser) # 爬取一个机场的详细信息
i = i + 1
# 关闭浏览器,浏览器每爬取一个页面上的所有机场后重启一次
# 这是因为如果不关闭浏览器,连续访问页面,会导致内存占用增加(虽然只是在一个标签页里请求资源)
# 我尝试过打开新的标签页,删除旧的标签,在新的标签页里请求资源,但内存还是会增加,只是增加的速度慢了
# 把浏览器关了重启是最稳妥的方法了
# 总共有8662个页面,如果连续爬取,会把内存撑爆的
browser.quit()
except:
print("从 " + PageURL + "获取数据失败")
browser.quit()
return 0
def getAirportDetailInfo(airportDetailURL, browser): # 从获取的机场列表中获取每个机场的详细信息
try:
time.sleep(3) # 间隔3秒访问一次,访问服务器速度太快会被禁的,设置为1秒或2秒会在访问100多个页面后被禁
browser.get(airportDetailURL) # 浏览器请求页面
html = browser.page_source
soup = BeautifulSoup(html, 'html.parser')
tag_trs = soup.table.table.find_all('tr')
airportInfoDict = {}
for tr in tag_trs:
airportInfoDict[tr.td.string] = tr.td.next_sibling.next_sibling.span.string
airportInfoList.append(airportInfoDict)
except:
print("从 " + airportDetailURL + "获取数据失败")
return 0
def writeInfoToFile(infoFilepath):
with open(infoFilepath, 'w+', encoding='utf-8') as infoFile:
infoFile.write(json.dumps(airportInfoList, ensure_ascii=False))
def main(): # 读取机场URL文件,获取机场列表
infoFilePath = "test.json"
page = 1 # 打印爬取进度用,爬取程序很漫长的,没有提示信息很恐怖的
with open("test.txt", 'r') as AirportFile:
for PageURL in AirportFile:
print("第:" + str(page) + "/298 个页面")
getAirportInfo(PageURL, page) # 获取一个页面中所有机场详细信息
page = page + 1
writeInfoToFile(infoFilePath) # 将机场详细信息列表里的数据写入json文件
if __name__ == '__main__':
main()