【爬虫案例】python爬取XX银行在每个地区的数量

任务如下:

爬取XX银行在全国各地的数量,该银行在XX省XX区XX市的数量。

(该网址没有robots.txt规则,若有需遵守规则)

只以两种银行为例,仅供学习使用。

A银行是从后端返回给前端是数据是json格式,也就是最常见的前后端分离模式。

做过后端项目的应该知道,这里返回的json数据就是DTO(数据传输对象)数据,当前端要我们返回某些数据的时候,我们不可能把所有数据(例如敏感数据或无关数据)全部都传输到前端,这样很容易就泄露大量的信息。于是就有了DTO,它封装了一部分可对外公开的数据,同时也可以节省流量。

我们能够获取到这个DTO,就能往下继续分析如何利用并爬取这类信息。

而B银行则是以htm结尾的静态页面,也就是不通过服务器编译解释直接送出给浏览器读取的静态网页,它没有返回数据,这类处理起来比较麻烦,后面会说。

 

 

 A银行

分析

先从A银行开始说。思路如下:随便输入地区,可以发现返回了一部分搜索结果。 

观察网址发现并没有变化。

来到F12网络那块,可以看到发送请求的地址,并且有明确的各种id。

试了几次之后,可以确定如下:

p=110000&c=110100&b=110101&q=&t=1&z=0&i=0

p是省 c是市 b是区县  z是业务类型  t是营业网点   q是关键字  i是搜索结果的页数

预览中也是正常的json数据

我们复制请求网址到浏览器,可以发现正常返回的json格式数据,这意味着我们可以通过python模拟访问这样的网址,从而获取到该请求返回的json数据,通过进一步筛选得到我们想要的数据。 

实现

 先写出获取页面的代码,使用json库转换为python的dict字典对象

import requests #网络请求
from bs4 import BeautifulSoup #页面解析

import json
from jsonpath import jsonpath

url = "https://app.abchina.com/branch/common/BranchService.svc/Branch?p=110000&c=110100&b=110101&q=&t=1&z=0&i=0"
# p是省 c是市 b是区县  z是业务类型  t是营业网点   q是关键字  i是搜索结果的页数
headers = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}
req = requests.get(url=url,headers=headers) #访问的地址,
req.encoding='utf-8'
txt = req.text#txt是str类型
# print(txt+"\n")

txt1 = json.loads(txt)
print(txt1)#loads转成字典类型

在一级目录下,能看到一个叫TotalCount的属性,和BranchSearchRests里的数据进行比对,可以知道这个就是当前区县所拥有的A银行数量,也就是北京市东城区的A银行数量。我们要的就是这个,但还不够,我们还需要其他的属性,用于我们后面的处理。 

打开BranchSearchRests,可以看到几个主要的数据,BroughId,CityId,ProvinceId,Name, 也就是我上面说的,对应到请求网址中的,p是省 c是市 b是区县,我们需要得到对应的Id,对他们逐个进行循环,就能够得到每一个区县的准确请求网址。对这些网址进行模拟访问,就能够得到每个区县的A银行数量。

 因为没有用过jsonpath,所以查了一下用法,参考Python3中JsonPath用法 - 漂泊的小虎 - 博客园

于是我们可以先输出一下看看是否能获取到准确数据,以Name和Address为例

list_name = jsonpath(txt1, '$..BranchSearchRests[*].BranchBank.Name')
print(list_name)

list_adress = jsonpath(txt1, '$..BranchSearchRests[*].BranchBank.Address')
print(list_adress)

 

回到F12网络页面,能发现初始页面还有其他其他的请求

 里面是关于的Id和名称,我们需要它出现在循环里面,所以将它拿下来。

url2="https://app.abchina.com/branch/common/BranchService.svc/District"
headers={"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"}
req=requests.get(url=url2,headers=headers) #访问的地址,
req.encoding='utf-8'
txt=req.text#txt是双引号
txt1 = json.loads(txt)
# print(txt1)
list_province_id = jsonpath(txt1, '$..Id')
list_province_name = jsonpath(txt1, '$..Name')
print(list_province_id)
print(list_province_name)
print(type(list_province_id[0]))

 此时,list_province_id列表里是str类型,为方便后面测试,先改成int型,需要的时候再转换成str。转换方法为新建一个列表,遍历原列表并强转然后赋给新列表,

list_num_province_id = list(int(x) for x in list_province_id)
print(type(list_num_province_id[0]))

 

这样我们就可以开始对省市进行循环了,如下

url = "https://app.abchina.com/branch/common/BranchService.svc/District"
try:
    headers = {
        "user-agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
    }
    r = requests.get(url, headers)
    r.encoding = 'utf-8'
    txt = r.text  #txt是双引号
    txt1 = json.loads(txt)
    #     print(txt1)
    list_province_id = jsonpath(txt1, '$..Id')
    list_num_province_id = list(int(x) for x in list_province_id)
    # 省市列表
    print(list_num_province_id)

    #对省市循环
    for province_id in list_num_province_id:
        # 这里用来测试,遍历到13000时候就先停止
        if (province_id > 130000):
            break
        # 拼接成地市的链接
        url1 = "https://app.abchina.com/branch/common/BranchService.svc/District/" + str(
            province_id)
        print(url1)
        r = requests.get(url1, headers)
        r.encoding = 'utf-8'
        txt = r.text  #txt是双引号
        txt1= json.loads(txt)
        print(txt1)
except:
    print("爬取失败")

 可以看到,对省市的链接进行访问,可以得到地市的id与name,那么继续使用地市id就可以获得区县的id与name。同样,对地市使用jsonpath获取id,转换成int,做循环。

url = "https://app.abchina.com/branch/common/BranchService.svc/District"
# try:
headers = {
    "user-agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
}
r = requests.get(url, headers)
r.encoding = 'utf-8'
txt = r.text  #txt是双引号
txt1 = json.loads(txt)
#     print(txt1)
list_province_id = jsonpath(txt1, '$..Id')
list_num_province_id = list(int(x) for x in list_province_id)
# 省市列表
print(list_num_province_id)

#对省市循环
for province_id in list_num_province_id:
    # 这里用来测试,遍历到13000时候就先停止
    if (province_id > 130000):
        break
    url1 = "https://app.abchina.com/branch/common/BranchService.svc/District/" + str(
        province_id)
#         print(url1)
    r = requests.get(url1, headers)
    r.encoding = 'utf-8'
    txt = r.text  #txt是双引号
    txt1= json.loads(txt)
    print(txt1)
    list_city_id = jsonpath(txt1, '$..Id')
    list_num_city_id = list(int(x) for x in list_city_id)

    # 对地市循环
    for district_id in list_num_city_id:
        url2 = "https://app.abchina.com/branch/common/BranchService.svc/District/any/" + str(
            district_id)
        print(url2)
        r = requests.get(url2, headers)
        r.encoding = 'utf-8'
        txt = r.text  #txt是双引号
        txt1 = json.loads(txt)
        print(txt1)

 地址和获取到的数据都正常,继续使用jsonpath选取id和name:
list_district_id = jsonpath(txt1, '$..Id')
list_district_name = jsonpath(txt1, '$..Name'),
然后循环内进行拼接
url3 = "https://app.abchina.com/branch/common/BranchService.svc/Branch?p=" +
          str(province_id) + "&c=" + str(district_id) + "&b=" + str(item2[0]) + "&q=&t=1&z=0&i=0"

这里的循环使用zip()函数zip()函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。

import requests #网络请求
from bs4 import BeautifulSoup #页面解析
import pandas as pd #保存数据
import time
import json
from jsonpath import jsonpath

# import urllib
url = "https://app.abchina.com/branch/common/BranchService.svc/District"

headers = {
    "user-agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
}
r = requests.get(url, headers)
r.encoding = 'utf-8'
txt = r.text  #txt是双引号
txt1 = json.loads(txt)
#     print(txt1)
list_province_id = jsonpath(txt1, '$..Id')
list_num_province_id = list(int(x) for x in list_province_id)
# 省市列表
print(list_num_province_id)

#对省市循环
for province_id in list_num_province_id:
    # 这里用来测试,遍历到13000时候就先停止
    if (province_id > 130000):
        break
    url1 = "https://app.abchina.com/branch/common/BranchService.svc/District/" + str(
        province_id)
#         print(url1)
    r = requests.get(url1, headers)
    r.encoding = 'utf-8'
    txt = r.text  #txt是双引号
    txt1= json.loads(txt)
    print(txt1)
    list_city_id = jsonpath(txt1, '$..Id')
    print("市区id")
    list_num_city_id = list(int(x) for x in list_city_id)
    print(list_num_city_id)
    # 对地市循环
    for district_id in list_num_city_id:
        url2 = "https://app.abchina.com/branch/common/BranchService.svc/District/any/" + str(
            district_id)
        print(url2)
        r = requests.get(url2, headers)
        r.encoding = 'utf-8'
        txt = r.text  #txt是双引号
        txt1 = json.loads(txt)
#         print(txt1)
        list_district_id = jsonpath(txt1, '$..Id')
        list_district_name = jsonpath(txt1, '$..Name')
#         print(list_district_id)
        print("市区名称:")
        print(list_district_name)
        list_num_district_id = list(int(x) for x in list_district_id)

        # 市区列表
        print("市区ID:")
        print(list_num_district_id)

        # Branch?p=110000&c=110100&b=110101&q=&t=1&z=0&i=0
        # 对市区循环
        # zip同时对id和name进行遍历,item2[0]表示选取参数中第0个的参数(id),[1]就代表第二个参数(name)
        for item2 in zip(list_num_district_id, list_district_name):
            url3 = "https://app.abchina.com/branch/common/BranchService.svc/Branch?p=" + str(province_id) + "&c=" + str(district_id) + "&b=" + str(item2[0]) + "&q=&t=1&z=0&i=0"
            # print("url3="+url3)
            r = requests.get(url3, headers)
            r.encoding = 'utf-8'
            txt = r.text  #txt是双引号
            txt1= json.loads(txt)
            # 获取该市区的银行数量
            totalcount = jsonpath(txt1, '$.TotalCount')
            # 拼接市区名字和数量输出
            print(item2[1] + "_" + str(totalcount))
        # 市区循环完后,对地市+100,直到地市也循环完
        district_id = district_id + 100
    # 做一个延时,降低连续爬取对服务器的压力
    time.sleep(1)

 运行之后可以看到我们要的数据了。但是代码仍然是有问题的,当我们爬取到海南省的时候,会发现报错了,原因在地市循环的时候,有些地市实际上是空的。他的 b为-1。

 

 

这时候,我们就需要加一个判断,当它为空时就拼接-1,不为空就正常拼接.

import requests #网络请求
from bs4 import BeautifulSoup #页面解析
import pandas as pd #保存数据
import time
import json
from jsonpath import jsonpath

# import urllib
url = "https://app.abchina.com/branch/common/BranchService.svc/District"

headers = {
    "user-agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
}
r = requests.get(url, headers)
r.encoding = 'utf-8'
txt = r.text  #txt是双引号
txt1 = json.loads(txt)
#     print(txt1)
list_province_id = jsonpath(txt1, '$..Id')
list_num_province_id = list(int(x) for x in list_province_id)
# 省市列表
print(list_num_province_id)

#对省市循环
for province_id in list_num_province_id:
    # 这里用来测试,
    # if(province_id < 460000 or province_id > 510000):
    #    continue
    url1 = "https://app.abchina.com/branch/common/BranchService.svc/District/" + str(
        province_id)
#         print(url1)
    r = requests.get(url1, headers)
    r.encoding = 'utf-8'
    txt = r.text  #txt是双引号
    txt1= json.loads(txt)
    print(txt1)
    list_city_id = jsonpath(txt1, '$..Id')
    print("地市id")
    list_num_city_id = list(int(x) for x in list_city_id)
    print(list_num_city_id)
    print("地市name")
    list_city_name = jsonpath(txt1, '$..Name')
    
    # 对地市循环
    for district in zip(list_num_city_id,list_city_name):
        district_id = district[0]
        district_name = district[1]
        url2 = "https://app.abchina.com/branch/common/BranchService.svc/District/any/" + str(
            district_id)
        #print("url2 = "+url2)
        r = requests.get(url2, headers)
        r.encoding = 'utf-8'
        txt = r.text  #txt是双引号
        txt1 = json.loads(txt)
        list_district_id = jsonpath(txt1, '$..Id')
        list_district_name = jsonpath(txt1, '$..Name')
        # 如果该地市为空(例如海南三沙市)
        if (list_district_id == False):
            b = -1
            url3 = "https://app.abchina.com/branch/common/BranchService.svc/Branch?p=" + str(
                province_id) + "&c=" + str(district_id) + "&b=" + str(
                    b) + "&q=&t=1&z=0&i=0"
            #print("url3="+url3)
            r = requests.get(url3, headers)
            r.encoding = 'utf-8'
            txt = r.text  #txt是双引号
            txt1 = json.loads(txt)
            totalcount = jsonpath(txt1, '$.TotalCount')
            this_city = jsonpath(txt1, '$...City')
            print(str(district_name) + str(this_city) + "_" + str(totalcount))
            district_id = district_id + 100
        else:
            print("市区名称:")
            print(list_district_name)
            list_num_district_id = list(int(x) for x in list_district_id)
            # 市区列表
            print("市区ID:")
            print(list_num_district_id)

            # Branch?p=110000&c=110100&b=110101&q=&t=1&z=0&i=0
            # 对市区循环
            # zip同时对id和name进行遍历
            for item2 in zip(list_num_district_id, list_district_name):
                url3 = "https://app.abchina.com/branch/common/BranchService.svc/Branch?p=" + str(province_id) + "&c=" + str(district_id) + "&b=" + str(item2[0]) + "&q=&t=1&z=0&i=0"
                print("url3="+url3)
                r = requests.get(url3, headers)
                r.encoding = 'utf-8'
                txt = r.text  #txt是双引号
                txt1= json.loads(txt)
                # 获取该市区的银行数量
                totalcount = jsonpath(txt1, '$.TotalCount')
                # 拼接市区名字和数量输出
                print(str(district_name) + "_" + item2[1] + "_" + str(totalcount))
            # 市区循环完后,对地市+100,直到地市也循环完
            district_id = district_id + 100
        # 做一个延时,降低连续爬取对服务器的压力
        time.sleep(1)

 至此,A银行的每一个地区的银行数量都爬取完毕。

================================分界线====================================

B银行

分析

实现

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值