Java爬虫-爬取疫苗批次信息

简介: 为了解决疫苗批号查询难题,作者因个人情况需查询脊髓灰质炎灭活疫苗信息,发现官网查询系统不便使用。于是,技术大展身手,编写了一个Java爬虫,利用Hutool、Jsoup和Spring Boot的MongoDB库,抓取并存储了中国食品药品检定研究院等多家机构近十年的疫苗批次信息。代码示例展示了如何爬取数据并存入MongoDB,方便按需查询。

今年3月份开始,就接到通知, 根据《关于开展有关人群第二剂次脊髓灰质炎灭活疫苗补种工作的通知》国疾控卫免发〔2024〕1号文件要求,在2016年3月1日至2019年9月30日之间出生的儿童,凡无接种禁忌者,需补齐2剂次脊髓灰质炎灭活疫苗。由于我家一直是异地打针【在外漂打工,懂的都懂】,疫苗本上信息又特别有限【吐槽-六七年前的疫苗本缺陷太大了:无厂家,无备注是否口服,无备注是灭活还是减毒】,上周去打针被问及6年前的第一针是注射还是口服,瞬间被问住了,记得3年前幼儿园入学前的打针就已经被工作人员问过一次了,问脊髓灰质炎疫苗第二、三针是注射还是口服的,甲肝疫苗是活疫苗还是灭活疫苗。。。
image.png

经过网上各种搜索,通过疫苗本上写的批号到网上查询追溯,最后发现在【中国食品药品检定研究院】https://bio.nifdc.org.cn/pqf/search.do?formAction=pqfQkcx上可以查询,但是这个查询也太难用了,该网站需要厂家+疫苗名+批号三个条件查询,但我只知道批号,其它信息一概不知。。。

作为技术人员,一怒之下,写了个爬虫,把该网站近十年公布的疫苗批次信息全都抓到本地。。。

上菜:

<!-- hutool工具类-->
<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-http</artifactId>
   <version>5.8.23</version>
</dependency>

<dependency>
   <groupId>org.jsoup</groupId>
   <artifactId>jsoup</artifactId>
   <version>1.17.2</version>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

/**
 * 获取疫苗批次
 * @author zhaokk
 * @since 2024/5/26
 */
public class GetVaccinBatch {
   
   

    public static String BASE_URL = "https://bio.nifdc.org.cn/pqf/";

    public static void main(String[] args) throws IOException {
   
   
        String[] listUrlArray = {
   
   
                //中国食品药品检定研究院
                "search.do?formAction=pqfGsByJG&parameter1=1",
                //北京市药品检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=5b6ea8c91cf9013d011cfdfbda100041",
                //上海市食品药品检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=4028813a1d225be5011d2265474b0004",
                //广东省药品检验所
                "search.do?formAction=pqfGsByJG&parameter1=4028813a1d225be5011d226a9159001c",
                //四川省药品检验研究院(四川省医疗器械检测中心)
                "search.do?formAction=pqfGsByJG&parameter1=4028813a1d225be5011d226ba310001e",
                //湖北省药品监督检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=4028813a1d225be5011d22697942001a",
                //吉林省药品检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=4028813a1d225be5011d226392100002",
                //甘肃省药品检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=4028813a1d225be5011d226c637d0020",
                //重庆市食品药品检验检测研究院
                "search.do?formAction=pqfGsByJG&parameter1=20190917c001",
                //山东省食品药品检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=20190924c001",
                //辽宁省药品检验检测院
                "search.do?formAction=pqfGsByJG&parameter1=20210315c001",
                //云南省食品药品监督检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=20210926c001",
                //河北省药品医疗器械检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=20211011c001",
                //浙江省食品药品检验研究院
                "search.do?formAction=pqfGsByJG&parameter1=20210210c002"
        };

        MongoDbUtils.connect("mongodb://127.0.0.1:27017", "vaccin-batch");
        for (String listUrl : listUrlArray) {
   
   
            //发送http请求
            Document document = Jsoup.connect(BASE_URL+listUrl).get();
            Elements aList = document.select("table tr td > a");

            for (int i = aList.size()-1; i >= 0; i--) {
   
   
                Element a = aList.get(i);
                String atext = a.text();
                String ahref = a.attr("href");
                String publishDateStr = atext.substring(atext.length()-11, atext.length()-1);
                System.out.println(atext + ":" + ahref);
                System.out.println("公布日期:" + publishDateStr);

                org.bson.Document saveLogDoc = new org.bson.Document();
                saveLogDoc.append("notice_list_url", BASE_URL+listUrl);
                saveLogDoc.append("notice_detail_url", BASE_URL+ahref);
                saveLogDoc.append("notice_title", atext);
                List<org.bson.Document> saveLogList = MongoDbUtils.findBy("vaccin-batch-savelog", saveLogDoc);
                if(!saveLogList.isEmpty()){
   
   
                    System.out.println(BASE_URL+ahref + "【"+ atext + "】已存在,跳过");
                    continue;
                }

                viewDetail(BASE_URL+ahref, atext);
                saveLogDoc.append("publish_date", publishDateStr);
                saveLogDoc.append("create_time", DateUtil.now());
                MongoDbUtils.insert("vaccin-batch-savelog", saveLogDoc);

            }
        }

    }

    public static void viewDetail(String noticeDetailUrl, String noticeTitle) throws IOException {
   
   
//        Document document = Jsoup.connect(noticeDetailUrl).get();
        Connection.Response resp = Jsoup.connect(noticeDetailUrl)
                .timeout(60000)
                .method(Connection.Method.GET)
                .maxBodySize(0)
                .followRedirects(false)
                .execute();
        String htmlStr = new String(resp.bodyAsBytes());

        Document document = Jsoup.parse(htmlStr);
        Elements theadList = document.select("table thead tr");
        if(theadList.isEmpty() || theadList.size() != 2){
   
   
            throw new RuntimeException("未解析到信息");
        }
        Elements theadCols = theadList.get(1).select("td");

        Elements tbodyList = document.select("table thead + tbody tr");
        if(tbodyList.isEmpty()){
   
   
            throw new RuntimeException("未解析到信息");
        }
        for (Element row : tbodyList) {
   
   
            Elements cols = row.select("td");
            if(cols.size() != theadCols.size()){
   
   
//                break;
                System.out.println(document);
                System.out.println(noticeDetailUrl);
                System.out.println(row);
                throw new RuntimeException("未解析到正确的信息");
            }

            org.bson.Document mongoDoc = new org.bson.Document();
            for (int i = 0; i < cols.size(); i++) {
   
   
                String key = FieldEnum.getName(theadCols.get(i).text());
                if(StrUtil.isBlank(key)){
   
   
                    continue;
                }
                mongoDoc.append(key, cols.get(i).text());
            }
            mongoDoc.append("notice_title", noticeTitle);
            mongoDoc.append("notice_detail_url", noticeDetailUrl);
            //保存数据库
            MongoDbUtils.insert("vaccin-batch", mongoDoc);
        }
    }

}


/**
 * @author zhaokk
 * @since 2024/5/26
 */
public enum FieldEnum {
   
   

    PRODUCT_NAME("产品名称", "product_name"),
    SPEC("规格", "spec"),
    BATCH_NO("批号", "batch_no"),
    QUANTITY("签发量", "quantity"),
    VALID_DATE("有效期至", "valid_date"),
    PRODUCER("生产企业", "producer"),
    PRODUCER_ORG("上市许可持有人", "producer"),
    CHECK_NO("收检编号", "check_no"),
    CERT_NO("证书编号", "cert_no"),
    REPORT_NO("报告编号", "report_no"),
    SIGN_DATE("签发日期", "sign_date"),
    SIGN_REMARK("签发结论", "sign_remark"),
    SIGN_ORG("批签发机构", "sign_org")
    ;

    private String remark;
    private String name;

    FieldEnum(String remark, String name) {
   
   
        this.remark = remark;
        this.name = name;
    }

    public static String getName(String remark){
   
   
        for(FieldEnum value : FieldEnum.values()){
   
   
            if(remark.equals(value.getRemark())){
   
   
                return value.getName();
            }
        }
        return null;
    }

    public String getRemark() {
   
   
        return remark;
    }

    public void setRemark(String remark) {
   
   
        this.remark = remark;
    }

    public String getName() {
   
   
        return name;
    }

    public void setName(String name) {
   
   
        this.name = name;
    }
}

再搭配一道菜mogodb Util,不用跑什么tomcat,运行main函数直接就是开干,最后通过Navicat等工具连上随意检索。

image.png


import com.mongodb.BasicDBObject;
import com.mongodb.MongoWriteException;
import com.mongodb.client.*;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;

import java.util.ArrayList;
import java.util.List;

/**
 * MongoDb 操作类
 * @author zhaokui
 * 2018年1月31日 
 */
public class MongoDbUtils {
   
   

   private static MongoDatabase db;
    /** 
     * 链接数据库 
     *            
     * @param uri
     *            主机名 + 端口号
     * @param databaseName 
     *            数据库名称 
     *            
     */
    public static void connect(String uri, String databaseName) {
   
   
        MongoClient client = MongoClients.create(uri);
        db = client.getDatabase(databaseName);  
    } 

    public static MongoCollection<Document> getCollection(String collectionName){
   
   
       return db.getCollection(collectionName);
    }


    /** 
     * 插入一个文档 
     *  
     * @param document 
     *            文档 
     */  
    public static void insert(String collectionName, Document document) {
   
     
       getCollection(collectionName).insertOne(document);  
    }
    /** 
     * 插入一个文档 
     *  
     * @param document 
     *            文档 
     */  
    public static void insertv2(String collectionName, Document document) throws Exception {
   
     
          try{
   
   
             getCollection(collectionName).insertOne(document);
          }catch(MongoWriteException e) {
   
   
             e.printStackTrace();
          }
    }  

    /**
     * 查找对象 - 根据主键_id
     * 
     * @param collectionName
     * @param id
     * @return
     */
    public static Document findById(String collectionName, String id) {
   
   
        ObjectId _idobj = null;
        try {
   
   
            _idobj = new ObjectId(id);
        } catch (Exception e) {
   
   
            return null;
        }
        Document myDoc = getCollection(collectionName).find(Filters.eq("_id", _idobj)).first();
        return myDoc;
    }

    /** 
     * 查询所有文档 
     *  
     * @return 所有文档集合 
     */  
    public static List<Document> findAll(String collectionName) {
   
     
        List<Document> results = new ArrayList<Document>();  
        FindIterable<Document> iterables = getCollection(collectionName).find();  
        MongoCursor<Document> cursor = iterables.iterator();  
        while (cursor.hasNext()) {
   
     
            results.add(cursor.next());  
        }  

        return results;  
    }  

    /** 
     * 查询所有文档 
     *  
     * @return 所有文档集合 
     */  
    public static List<Document> findAll(String collectionName, Bson orderBy) {
   
     
        List<Document> results = new ArrayList<Document>();  
        FindIterable<Document> iterables = getCollection(collectionName).find().sort(orderBy);  
        MongoCursor<Document> cursor = iterables.iterator();  
        while (cursor.hasNext()) {
   
     
            results.add(cursor.next());  
        }  

        return results;  
    } 

    /** 
     * 根据条件查询 
     *  
     * @param filter 
     *            查询条件 //注意Bson的几个实现类,BasicDBObject, BsonDocument, 
     *            BsonDocumentWrapper, CommandResult, Document, RawBsonDocument 
     * @return 返回集合列表 
     */  
    public static List<Document> findBy(String collectionName, Bson filter) {
   
     
        List<Document> results = new ArrayList<Document>();  
        FindIterable<Document> iterables = getCollection(collectionName).find(filter);
        MongoCursor<Document> cursor = iterables.iterator();  
        while (cursor.hasNext()) {
   
     
            results.add(cursor.next());  
        }  

        return results;  
    }  

    /** 
     * 根据条件查询 + 排序
     *  
     * @param filter 
     *            查询条件 //注意Bson的几个实现类,BasicDBObject, BsonDocument, 
     *            BsonDocumentWrapper, CommandResult, Document, RawBsonDocument 
     * @return 返回集合列表 
     */  
    public static List<Document> findBy(String collectionName, Bson filter, Bson orderBy) {
   
     
        List<Document> results = new ArrayList<Document>();  
        FindIterable<Document> iterables = getCollection(collectionName).find(filter).sort(orderBy);  
        MongoCursor<Document> cursor = iterables.iterator();  
        while (cursor.hasNext()) {
   
     
            results.add(cursor.next());  
        }  

        return results;  
    }  

    public static List<Document> findBy(String collectionName, Bson filter, Bson orderBy, int pageSize) {
   
     
        List<Document> results = new ArrayList<Document>();  
        FindIterable<Document> iterables = getCollection(collectionName).find(filter).sort(orderBy).limit(pageSize);  
        MongoCursor<Document> cursor = iterables.iterator();  
        while (cursor.hasNext()) {
   
     
            results.add(cursor.next());  
        }  
        return results;  
    }  

    /** 统计数 */
    public static long getCount(String collectionName, Bson filter) {
   
   
        return getCollection(collectionName).countDocuments(filter);
    }

    /** 分页查询 */
    public static List<Document> findByPage(String collectionName, Bson filter, int pageNo, int pageSize) {
   
   
       List<Document> results = new ArrayList<Document>();  
        Bson orderBy = new BasicDBObject("_id", -1);
        MongoCursor<Document> cursor = getCollection(collectionName).find(filter).sort(orderBy).skip((pageNo - 1) * pageSize).limit(pageSize).iterator();
        while (cursor.hasNext()) {
   
     
            results.add(cursor.next());  
        } 
        return results;  
    }

    /** 分页查询+排序 */
    public static List<Document> findByPage(String collectionName, Bson filter, Bson orderBy, int pageNo, int pageSize) {
   
   
       List<Document> results = new ArrayList<Document>();  
        MongoCursor<Document> cursor = getCollection(collectionName).find(filter).sort(orderBy).skip((pageNo - 1) * pageSize).limit(pageSize).iterator();
        while (cursor.hasNext()) {
   
     
            results.add(cursor.next());  
        } 
        return results;  
    }

    /** 
     * 更新查询到的第一个 
     *  
     * @param filter 
     *            查询条件 
     * @param update 
     *            更新文档 
     * @return 更新结果 
     */  
    public static UpdateResult updateOne(String collectionName, Bson filter, Bson update) {
   
     
        UpdateResult result = getCollection(collectionName).updateOne(filter, update);  

        return result;  
    }  

    /** 
     * 更新查询到的所有的文档 
     *  
     * @param filter 
     *            查询条件 
     * @param update 
     *            更新文档 
     * @return 更新结果 
     */  
    public static UpdateResult updateMany(String collectionName, Bson filter, Bson update) {
   
     
        UpdateResult result = getCollection(collectionName).updateMany(filter, update);  

        return result;  
    }  

    /**
     * FIXME
     * 
     * @param collectionName
     * @param id
     * @param newdoc
     * @return
     */
    public static Document updateById(String collectionName, String id, Document newdoc) {
   
   
        ObjectId _idobj = null;
        try {
   
   
            _idobj = new ObjectId(id);
        } catch (Exception e) {
   
   
            return null;
        }
        Bson filter = Filters.eq("_id", _idobj);
        // coll.replaceOne(filter, newdoc); // 完全替代
        getCollection(collectionName).updateOne(filter, new Document("$set", newdoc));
        return newdoc;
    }

    /** 
     * 更新一个文档, 结果是replacement是新文档,老文档完全被替换 
     *  
     * @param filter 
     *            查询条件 
     * @param replacement 
     *            跟新文档 
     */  
    public static void replace(String collectionName, Bson filter, Document replacement) {
   
     
        getCollection(collectionName).replaceOne(filter, replacement);  
    }  

    /** 
     * 根据条件删除一个文档 
     *  
     * @param filter 
     *            查询条件 
     */  
    public static void deleteOne(String collectionName, Bson filter) {
   
     
        getCollection(collectionName).deleteOne(filter);  
    }  

    /** 
     * 根据条件删除多个文档 
     *  
     * @param filter 
     *            查询条件 
     */  
    public static void deleteMany(String collectionName, Bson filter) {
   
     
        getCollection(collectionName).deleteMany(filter);  
    } 

    /**
     * 通过ID删除
     * 
     * @param collectionName
     * @param id
     * @return
     */
    public static long deleteById(String collectionName, String id) {
   
   
       long count = 0;
        ObjectId _id = null;
        try {
   
   
            _id = new ObjectId(id);
        } catch (Exception e) {
   
   
            return 0;
        }
        Bson filter = Filters.eq("_id", _id);
        DeleteResult deleteResult = getCollection(collectionName).deleteOne(filter);
        count = deleteResult.getDeletedCount();
        return count;
    }

}
相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
4天前
|
前端开发 Java
【技术进阶】Java高手都在用的秘籍:自定义异常,让错误信息说话!
【6月更文挑战第19天】在Java中,自定义异常提升代码可读性和可维护性,提供针对特定错误的定制反馈。创建自定义异常涉及继承`Exception`类,如`CustomException`,并用它来抛出具有详细信息的错误。在实践中,可以为异常添加额外字段,如`DetailedException`的`errorCode`,以增强信息携带能力。使用自定义异常时,应明确目的、保持简洁、提供丰富信息并统一风格,使其成为高效错误处理的工具。
|
8天前
|
前端开发 JavaScript Java
计算机Java项目|游戏美术外包管理信息系统
计算机Java项目|游戏美术外包管理信息系统
|
11天前
|
数据采集 存储 JSON
豆瓣电影信息爬虫实战-2024年6月
使用Python和`requests`、`PyQuery`库,本文教程教你如何编写一个豆瓣电影列表页面的爬虫,抓取电影标题、导演、主演等信息。首先确保安装所需库,然后了解技术栈,包括Python、Requests、PyQuery和正则表达式。爬虫逻辑包括发送HTTP请求、解析HTML、提取数据。代码示例展示了如何实现这一过程,最后运行爬虫并将结果保存为JSON文件。注意遵守网站使用条款和应对反爬策略。
32 2
|
24天前
|
文字识别 Java API
Java图片信息识别
【5月更文挑战第26天】Java图片信息识别
25 1
|
8天前
|
前端开发 JavaScript Java
计算机Java项目|Springboot社区医院信息平台
计算机Java项目|Springboot社区医院信息平台
|
8天前
|
Java
创建java文件 自动添加作者、时间等信息 – IDEA 技巧
创建java文件 自动添加作者、时间等信息 – IDEA 技巧
9 0
|
16天前
|
Java
Java Socket编程 - 获取WEB站点主页信息
Java Socket编程 - 获取WEB站点主页信息
11 0
|
19天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的java语言的考试信息报名系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的java语言的考试信息报名系统附带文章源码部署视频讲解等
20 0
|
25天前
|
安全 Java 网络安全
Java中的异常处理:理解、实践与最佳实践云端防御线:云计算环境下的网络安全与信息保护
【5月更文挑战第28天】 在编程中,异常是无法避免的一部分,特别是在Java这种静态类型语言中。本文将深入探讨Java中的异常处理机制,包括如何理解异常,如何在代码中实现异常处理,以及一些关于异常处理的最佳实践。我们将通过实例和代码片段来阐述这些概念,以帮助读者更好地理解和应用Java的异常处理。 【5月更文挑战第28天】 随着企业数字化转型的深入,云计算服务已成为支撑现代业务的关键基础设施。然而,云服务的广泛采用也带来了前所未有的安全挑战。本文深入探讨了在云计算环境中维护网络安全和信息保护的策略和技术,分析了云服务模型对安全措施的影响,并提出了综合的安全框架以应对不断演变的威胁。通过采用加
|
26天前
|
IDE Java 开发工具
"如何使用 jconsole 查看Java进程中线程的详细信息? "
当Java程序运行时,其中的一些线程也正在执行。我们可以用第三方工具 jconsole 来查看Java进程中线程的执行情况和详细信息,这有助于我们对多线程编程的理解。
26 0