爬虫实例之爬取北京地铁站点

目标网站:https://www.bjsubway.com/station/zjgls/#

使用模块:reosrequestsBeautifulSoup

老样子,直接步入正题。

先看下结果:

北京地铁各站点间距离一览

       

              

 

  1. 载入模块
    import requests
    from bs4 import BeautifulSoup
    import re
    import os
  2. 接着接入网站,完成网站的解析。

    ul = 'https://www.bjsubway.com/station/zjgls/#'
    response = requests.get(ul)
    response.encoding = 'gbk'  # 原始网页编码错误,utf-8也不管用,只能用gbk
    html = response.text
    # print(html)
    soup = BeautifulSoup(html, 'lxml')  # 变成汤汁
    

    由于爬取的次数不多,该网站没有作直接限制,个人猜测是有次数限制的。至于次数是多少,感兴趣的朋友可以拿自己的IP撞撞枪口[/doge]。保险的方法是加上自己的User Agent,这一点,可以从浏览器找到,以后再讲讲。

  3. 对了,爬取的时候遇到一个网站编码的问题。使用requests爬取的时候,Requests 会基于 HTTP 头部对响应的编码作出有根据的推测。即requests会对网站进行自动编码,以保证人能看得懂,所以一般不用再另外编码。但这个网站爬取的结果里的中文却是乱码,将其重新编码为"utf-8",发现更乱了,于是改成了“gbk”。 

  4. 对于requests编码的问题,请参见:http://cn.python-requests.org/zh_CN/latest/user/quickstart.html#id3

  5. 我们想要的“菜”是线路名称以及站点信息。分析网址发现,我们的“菜”被装在“<td colspan="5" ",即“td”tag里面。

    一号线示例

    但这么找之后发现,线路不全。比如2号线就没找到,再分析2号线的代码:

    2号线

    那再把colspan = '7'加进去吧,结果发现还少几条线。原来还有colspan = '6' 和colspan = '9'(十号线,仅此一条)。这样一来,重复的代码有点多,写成一个函数:

    def get_txt_name():  # 得到线路名称的前一步
        txt_src_name = []
        for i in range(5, 10):
            temp = soup.find_all('td', {'colspan': str(i)})
            txt_src_name += temp
        return txt_src_name
        # 格式如[<td colspan="6">15号线相邻站间距信息统计表</td>, <td colspan="6">昌平线相邻站间距信息统计表</td>]
    # print(get_txt_name())  # 测试用
    def get_txtuseful_name():  # 得到可用的线路名称
        obj = []
        for each in get_txt_name():
            temp = re.findall(r">(.+?)<", str(each))  # 从>匹配到<(不包含),若要包含,则先使用re.compile,再search
            obj += temp
        return obj
    # print(get_txtuseful_name())  # 测试用

    得到的就是包含所有线路名称的列表啦。

  6. 同样地,分析网页发现所有地站点信息都被存储在“<tbody>”这个tag中,于是同样地进行查找并筛选:

    Stationinfo = soup.find_all('tbody')  # 汤汁是ResultSet,即结果集
    def get_stationinfo():
        obj = []
        for each in Stationinfo:
            temp = re.findall(r">(.+?)<", str(each))   # 正则匹配,str格式
            obj += temp
        return obj
    # print(get_stationinfo())

    到了tbody之后,剩下的数据就没有tag可用了,只能用正则表达式来匹配。查找">xxxx<"的"xxx"即为站点信息。

    还有一点要注意,要把soup中的ResultSet转换成str格式才能调用正则表达式。 

     

  7. 至此,所需要的数据已经全部拿到,接着就是文件的写入了:

    station_list = get_stationinfo()
    # print(station_list)
    os.makedirs('./线路图/', exist_ok=True)
    with open('./线路图/test.txt', 'w') as f:  # 不能是wb,编码有问题,或者str转换成byte
        for line in station_list:
            if line == '上行/下行':
                f.write(line + '\n')
            else:
                f.write(line + '      ')  # 多来几个空格显得好看一点

    还是编码问题,前面是str格式的,如果写入文件的格式为'b',即二进制,则会报错。解决的方法是第一像代码那样仅仅是'w'模式 ;或者在此之前将station_list转换成byte格式,那么使用'wb'模式就不会有问题了。

  8. 放一个整体代码:

    import requests
    from bs4 import BeautifulSoup
    import re
    import os
    
    ul = 'https://www.bjsubway.com/station/zjgls/#'
    response = requests.get(ul)
    response.encoding = 'gbk'  # 原始网页编码错误,utf-8也不管用,只能用gbk
    html = response.text
    # print(html)
    soup = BeautifulSoup(html, 'lxml')  # 变成汤汁
    
    
    def get_txt_name():  # 得到线路名称的前一步
        txt_src_name = []
        for i in range(5, 10):
            temp = soup.find_all('td', {'colspan': str(i)})
            txt_src_name += temp
        return txt_src_name
        # 格式如[<td colspan="6">15号线相邻站间距信息统计表</td>, <td colspan="6">昌平线相邻站间距信息统计表</td>]
    # print(get_txt_name())  # 测试用
    def get_txtuseful_name():  # 得到可用的线路名称
        obj = []
        for each in get_txt_name():
            temp = re.findall(r">(.+?)<", str(each))  # 从>匹配到<(不包含),若要包含,则先使用re.compile,再search
            obj += temp
        return obj
    # print(get_txtuseful_name())  # 测试用
    
    Stationinfo = soup.find_all('tbody')
    def get_stationinfo():
        obj = []
        for each in Stationinfo:
            temp = re.findall(r">(.+?)<", str(each))
            obj += temp
        return obj
    # print(get_stationinfo())
    
    
    station_list = get_stationinfo()
    # print(station_list)
    os.makedirs('./线路图/', exist_ok=True)
    with open('./线路图/test.txt', 'w') as f:  # 不能是wb,编码有问题,或者str转换成byte
        for line in station_list:
            if line == '上行/下行':
                f.write(line + '\n')
            else:
                f.write(line + '      ')  # 多来几个空格显得好看一点
    
  9. 不过本次爬虫还有一个小问题,就是没有将线路分开。想了一会没想到什么好的思路就没继续,可惜了我辛辛苦苦筛选出来的线路名称。知道的朋友可以评论区说明一下,感谢!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值