「原创」如何快速获取银行、联行号的数据?

在这里插入图片描述

前言

  经过一段时间的加班,终于是把项目熬上线了。本以为可以轻松一点,但往往事与愿违,出现了各种各样的问题。由于做的是POS前置交易系统,涉及到和商户进件以及交易相关的业务,需要向上游支付机构上送“联行号”,但是由于系统内的数据不全,经常出现找不到银行或者联行号有误等情况,导致无法进件。

  为了解决这个问题,我找上游机构要了一份支行信息。好家伙,足足有14w条记录。在导入系统时,发现有一些异常的数据。有些是江西的银行,地区码竟然是北京的。经过一段时间排查,发现这样的数据还挺多的。这可愁死我了,本来偷个懒,等客服反馈的时候,出现一条修一条。

  经过2分钟的思考,想到以后每天都要修数据,那不得烦死。于是长痛不如短痛,还不如一次性修了。然后我反手就打开了百度,经过一段时间的遨游。发现下面3个网站的支行信息比较全,准备用来跟系统内数据作对比,然后进行修正。

  • http://www.jsons.cn/banknum/
  • http://www.5cm.cn/bank/支行编号/
  • https://www.appgate.cn/branch/bankBranchDetail/支行编号

分析网站

  输入联行号,然后选择查询方式,点击开始查询就可以。但是呢,结果页面一闪而过,然后被广告页面给覆盖了,这个时候就非常你的手速了。对于这样的,自然是难不倒我。从前端的角度分析,很明显展示结果的table标签被隐藏了,用来显示广告。于是反手就是打开控制台,查看源代码。

经过一顿搜寻,终于是找到了详情页的地址。

  通过上面的操作,我们要想爬到数据,需要做两步操作。先输入联行号进行查询,然后进去详情页,才能取到想要的数据。所以第一步需要先获取查询的接口,于是我又打开了熟悉的控制台。

  从上图可以发现这些请求都是在获取广告,并没有发现我们想要的接口,这个是啥情况,难道凭空变出来的嘛。并不是,主要是因为这个网站不是前后端分离的,所以这个时候我们需要从它的源码下手。

<html>
 <body>
  <form id="form1" class="form-horizontal" action="/banknum/" method="post"> 
   <div class="form-group"> 
    <label class="col-sm-2 control-label"> 关键词:</label> 
    <div class="col-sm-10"> 
     <input class="form-control" type="text" id="keyword" name="keyword" value="102453000160"  placeholder="请输入查询关键词,例如:中关村支行" maxlength="50" /> 
    </div> 
   </div> 
   <div class="form-group"> 
    <label class="col-sm-2 control-label"> 搜索类型:</label> 
    <div class="col-sm-10"> 
     <select class="form-control" id="txtflag" name="txtflag"> 
     		<option value="0">支行关键词</option>
      	<option value="1" selected="">银行联行号</option>
      	<option value="2">支行网点地址</option> 
      </select> 
    </div> 
   </div> 
   <div class="form-group"> 
    <label class="col-sm-2 control-label"> </label> 
    <div class="col-sm-10"> 
     <button type="submit" class="btn btn-success"> 开始查询</button> 
     <a href="/banknum/" class="btn btn-danger">清空输入框</a> 
    </div> 
   </div> 
  </form>
 </body>
</html>

通过分析代码可以得出:

  • 请求地址:http://www.jsons.cn/banknum/
  • 请求方式:POST
  • 请求参数:
    • keyword: 联行号
    • txtflag :1

我们可以用PostMan来验证一下接口是否有效,验证结果如下图所示:

  剩下的两个网站相对比较简单,只需要更改相应的联行号,进行请求就可以获取到相应的数据,所以这里不过多赘述。

爬虫编写

  经过上面的分析了,已经取到了我们想要的接口,可谓是万事俱备,只欠代码了。爬取原理很简单,就是解析HTML元素,然后获取到相应的属性值保存下来就好了。由于使用Java进行开发,所以选用Jsoup来完成这个工作。

<!-- HTML解析器 -->
<dependency>
  <groupId>org.jsoup</groupId>
  <artifactId>jsoup</artifactId>
  <version>1.13.1</version>
</dependency>

  由于单个网站的数据可能不全,所以我们需要逐个进行抓取。先抓取第一个,如果抓取不到,则抓取下一个网站,这样依次进行下去。这样的业务场景,我们可以使用变种的责任链设计模式来进行代码的编写。

BankBranchVO支行信息
@Data
@Builder
public class BankBranchVO {

    /**
     * 支行名称
     */
    private String bankName;

    /**
     * 联行号
     */
    private String bankCode;

    /**
     * 省份
     */
    private String provName;

    /**
     * 市
     */
    private String cityName;

}
BankBranchSpider抽象类
public abstract class BankBranchSpider {

    /**
     * 下一个爬虫
     */
    private BankBranchSpider nextSpider;

    /**
     * 解析支行信息
     *
     * @param bankBranchCode 支行联行号
     * @return 支行信息
     */
    protected abstract BankBranchVO parse(String bankBranchCode);

    /**
     * 设置下一个爬虫
     *
     * @param nextSpider 下一个爬虫
     */
    public void setNextSpider(BankBranchSpider nextSpider) {
        this.nextSpider = nextSpider;
    }

    /**
     * 使用下一个爬虫
     * 根据爬取的结果进行判定是否使用下一个网站进行爬取
     *
     * @param vo 支行信息
     * @return true 或者 false
     */
    protected abstract boolean useNextSpider(BankBranchVO vo);

    /**
     * 查询支行信息
     *
     * @param bankBranchCode 支行联行号
     * @return 支行信息
     */
    public BankBranchVO search(String bankBranchCode) {
        BankBranchVO vo = parse(bankBranchCode);
        while (useNextSpider(vo) && this.nextSpider != null) {
            vo = nextSpider.search(bankBranchCode);
        }
        if (vo == null) {
            throw new SpiderException("无法获取支行信息:" + bankBranchCode);
        }
        return vo;
    }

}

  针对不同的网站解析方式不太一样,简言之就是获取HTML标签的属性值,对于这步可以有很多种方式实现,下面贴出我的实现方式,仅供参考。

JsonCnSpider
@Slf4j
public class JsonCnSpider extends BankBranchSpider {

    /**
     * 爬取URL
     */
    private static final String URL = "http://www.jsons.cn/banknum/";


    @Override
    protected BankBranchVO parse(String bankBranchCode) {

        try {
            log.info("json.cn-支行信息查询:{}", bankBranchCode);

            // 设置请求参数
            Map<String, String> map = new HashMap<>(2);
            map.put("keyword", bankBranchCode);
            map.put("txtflag", "1");

            // 查询支行信息
            Document doc = Jsoup.connect(URL).data(map).post();


            Elements td = doc.selectFirst("tbody")
                    .selectFirst("tr")
                    .select("td");

            if (td.size() < 3) {
                return null;
            }

            // 获取详情url
            String detailUrl = td.get(3)
                    .selectFirst("a")
                    .attr("href");

            if (StringUtil.isBlank(detailUrl)) {
                return null;
            }

            log.info("json.cn-支行详情-联行号:{}, 详情页:{}", bankBranchCode, detailUrl);

            // 获取详细信息
            Elements footers = Jsoup.connect(detailUrl).get().select("blockquote").select("footer");

            String bankName = footers.get(1).childNode(2).toString();
            String bankCode = footers.get(2).childNode(2).toString();
            String provName = footers.get(3).childNode(2).toString();
            String cityName = footers.get(4).childNode(2).toString();

            return BankBranchVO.builder()
                    .bankName(bankName)
                    .bankCode(bankCode)
                    .provName(provName)
                    .cityName(cityName)
                    .build();

        } catch (IOException e) {
            log.error("json.cn-支行信息查询失败:{}, 失败原因:{}", bankBranchCode, e.getLocalizedMessage());
            return null;
        }
    }

    @Override
    protected boolean useNextSpider(BankBranchVO vo) {
        return vo == null;
    }

}
FiveCmSpider
@Slf4j
public class FiveCmSpider extends BankBranchSpider {

    /**
     * 爬取URL
     */
    private static final String URL = "http://www.5cm.cn/bank/%s/";

    @Override
    protected BankBranchVO parse(String bankBranchCode) {
        log.info("5cm.cn-查询支行信息:{}", bankBranchCode);

        try {
            Document doc = Jsoup.connect(String.format(URL, bankBranchCode)).get();
            Elements tr = doc.select("tr");

            Elements td = tr.get(0).select("td");
            if ("".equals(td.get(1).text())) {
                return null;
            }

            String bankName = doc.select("h1").get(0).text();
            String provName = td.get(1).text();
            String cityName = td.get(3).text();

            return BankBranchVO.builder()
                    .bankName(bankName)
                    .bankCode(bankBranchCode)
                    .provName(provName)
                    .cityName(cityName)
                    .build();

        } catch (IOException e) {
            log.error("5cm.cn-支行信息查询失败:{}, 失败原因:{}", bankBranchCode, e.getLocalizedMessage());
            return null;
        }
    }

    @Override
    protected boolean useNextSpider(BankBranchVO vo) {
        return vo == null;
    }

}

AppGateSpider
@Slf4j
public class AppGateSpider extends BankBranchSpider {

    /**
     * 爬取URL
     */
    private static final String URL = "https://www.appgate.cn/branch/bankBranchDetail/";

    @Override
    protected BankBranchVO parse(String bankBranchCode) {
        try {
            log.info("appgate.cn-查询支行信息:{}", bankBranchCode);

            Document doc = Jsoup.connect(URL + bankBranchCode).get();
            Elements tr = doc.select("tr");

            String bankName = tr.get(1).select("td").get(1).text();

            if(Boolean.FALSE.equals(StringUtils.hasText(bankName))){
                return null;
            }

            String provName = tr.get(2).select("td").get(1).text();
            String cityName = tr.get(3).select("td").get(1).text();

            return BankBranchVO.builder()
                    .bankName(bankName)
                    .bankCode(bankBranchCode)
                    .provName(provName)
                    .cityName(cityName)
                    .build();

        } catch (IOException e) {
            log.error("appgate.cn-支行信息查询失败:{}, 失败原因:{}", bankBranchCode, e.getLocalizedMessage());
            return null;
        }
    }

    @Override
    protected boolean useNextSpider(BankBranchVO vo) {


        return vo == null;
    }
}

初始化爬虫
@Component
public class BankBranchSpiderBean {

    @Bean
    public BankBranchSpider bankBranchSpider() {
        JsonCnSpider jsonCnSpider = new JsonCnSpider();
        FiveCmSpider fiveCmSpider = new FiveCmSpider();
        AppGateSpider appGateSpider = new AppGateSpider();
        jsonCnSpider.setNextSpider(fiveCmSpider);
        fiveCmSpider.setNextSpider(appGateSpider);
        return jsonCnSpider;
    }
}
爬取接口
@RestController
@AllArgsConstructor
@RequestMapping("/bank/branch")
public class BankBranchController {

    private final BankBranchSpider bankBranchSpider;

    /**
     * 查询支行信息
     *
     * @param bankBranchCode 支行联行号
     * @return 支行信息
     */
    @GetMapping("/search/{bankBranchCode}")
    public BankBranchVO search(@PathVariable("bankBranchCode") String bankBranchCode) {
        return bankBranchSpider.search(bankBranchCode);
    }

}
演示

爬取成功

爬取失败的情况

代码地址

  • https://gitee.com/huangxunhui/java-spider-data.git

总结

   这个爬虫的难点主要是在于Jsons.cn。因为数据接口被隐藏在代码里面,所以想取到需要花费一些时间。并且请求地址和页面地址一致,只是请求方式不一样,容易被误导。比较下来其他的两个就比较简单,直接替换联行号就可以了,还有就是这个三个网站也没啥反扒的机制,所以很轻松的就拿到了数据。

往期回顾

结尾

  如果觉得对你有帮助,可以多多评论,多多点赞哦,也可以到我的主页看看,说不定有你喜欢的文章,也可以随手点个关注哦,谢谢。

  我是不一样的科技宅,每天进步一点点,体验不一样的生活。我们下期见!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 要用 Python 获取银行联行,您需要找到一个数据源,并使用 Python 进行爬取和处理。通常,银行联行数据可以在政府网站或金融机构网站上获得,您可以使用 Python 中的爬虫库(如 BeautifulSoup)进行爬取。 在处理完数据之后,您可以使用 Python 中的库(如 pandas)将数据存储到 DataFrame 中,并使用 pandas 的 to_excel 方法将数据导出到 Excel 文件中。 代码示例: ``` import requests from bs4 import BeautifulSoup import pandas as pd # 获取数据 url = "https://www.example.com/bank-codes" res = requests.get(url) soup = BeautifulSoup(res.text, "html.parser") # 处理数据 bank_codes = [] table = soup.find("table") for row in table.find_all("tr"): cols = row.find_all("td") if len(cols) >= 2: bank_name = cols[0].text.strip() bank_code = cols[1].text.strip() bank_codes.append([bank_name, bank_code]) # 将数据存储到 DataFrame 中 df = pd.DataFrame(bank_codes, columns=["银行名称", "银行联行"]) # 导出到 Excel 文件中 df.to_excel("bank_codes.xlsx", index=False) ``` 请注意,此代码仅作为示例,实际情况可能有所不同,您需要根据您所使用的数据源进行相应的修改。 ### 回答2: 用Python获取银行联行并导出到Excel文件需要以下步骤: 1. 导入所需模块:使用Python,我们需要导入一些模块,如`requests`用于发送HTTP请求,`beautifulsoup4`用于爬取网页数据,`openpyxl`用于操作Excel文件。 2. 发送HTTP请求获取网页数据:使用`requests`模块发送HTTP请求,访问包含银行联行的网页,并获取到网页的HTML内容。 3. 解析网页数据:使用`beautifulsoup4`模块解析网页的HTML内容,找到包含银行联行的表格或列表。 4. 提取银行联行:根据网页的结构,使用`beautifulsoup4`模块提取出银行联行,并将其保存到一个列表或字典中。 5. 创建并操作Excel文件:使用`openpyxl`模块创建一个新的Excel文件,设置表头和列的名称。遍历银行联行列表或字典,将数据写入Excel文件的相应单元格。 6. 保存Excel文件:将完成数据写入Excel文件后,使用`openpyxl`模块保存Excel文件,生成最终的导出文件。 以上是基本的步骤,具体代码可以根据实际需求进行调整和优化。这个过程需要一定的Python编程基础和对相关模块的了解。 ### 回答3: 要通过Python获取银行联行并导出到Excel文件,可以采用以下步骤: 1. 导入所需的Python库,如pandas、openpyxl和requests。 ```python import pandas as pd import openpyxl import requests ``` 2. 创建一个函数,用于获取银行联行。这可以通过调用一个提供银行数据的API来实现。以下是一个示例函数: ```python def get_bank_info(bank_name): api_url = "https://api.example.com/bank_info" params = {"name": bank_name} response = requests.get(api_url, params=params) bank_info = response.json() return bank_info["bank_code"] ``` 这个函数接受银行名称作为参数,并返回对应的银行联行。 3. 创建一个空的数据框,用于保存银行名称和联行。 ```python bank_data = pd.DataFrame(columns=["银行名称", "联行"]) ``` 4. 循环遍历需要查询的银行列表,并调用上述函数获取联行,并将结果添加到数据框中。 ```python bank_list = ["工商银行", "建设银行", "中国银行"] for bank in bank_list: bank_code = get_bank_info(bank) bank_data = bank_data.append({"银行名称": bank, "联行": bank_code}, ignore_index=True) ``` 5. 将数据框保存为Excel文件。 ```python bank_data.to_excel("bank_info.xlsx", index=False) ``` 以上代码将数据保存为名为"bank_info.xlsx"的Excel文件,index=False表示不保存索引列。 完整的代码示例: ```python import pandas as pd import openpyxl import requests def get_bank_info(bank_name): api_url = "https://api.example.com/bank_info" params = {"name": bank_name} response = requests.get(api_url, params=params) bank_info = response.json() return bank_info["bank_code"] bank_data = pd.DataFrame(columns=["银行名称", "联行"]) bank_list = ["工商银行", "建设银行", "中国银行"] for bank in bank_list: bank_code = get_bank_info(bank) bank_data = bank_data.append({"银行名称": bank, "联行": bank_code}, ignore_index=True) bank_data.to_excel("bank_info.xlsx", index=False) ``` 运行以上代码后,将在当前目录下生成一个名为"bank_info.xlsx"的文件,其中包含了从API获取银行联行数据

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值