今年3月份開始,就接到通知, 根據《關於開展有關人群第二劑次脊髓灰質炎滅活疫苗補種工作的通知》國疾控衛免發〔2024〕1號檔案要求,在2016年3月1日至2019年9月30日之間出生的兒童,凡無接種禁忌者,需補齊2劑次脊髓灰質炎滅活疫苗。由於我家一直是異地打針【在外漂打工,懂的都懂】,疫苗本上資訊又特別有限【吐槽-六七年前的疫苗本缺陷太大了:無廠家,無備註是否口服,無備註是滅活還是減毒】,上週去打針被問及6年前的第一針是注射還是口服,瞬間被問住了,記得3年前幼兒園入學前的打針就已經被工作人員問過一次了,問脊髓灰質炎疫苗第二、三針是注射還是口服的,甲肝疫苗是活疫苗還是滅活疫苗。。。
經過網上各種搜尋,透過疫苗本上寫的批號到網上查詢追溯,最後發現在【中國食品藥品檢定研究院】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¶meter1=1", //北京市藥品檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=5b6ea8c91cf9013d011cfdfbda100041", //上海市食品藥品檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=4028813a1d225be5011d2265474b0004", //廣東省藥品檢驗所 "search.do?formAction=pqfGsByJG¶meter1=4028813a1d225be5011d226a9159001c", //四川省藥品檢驗研究院(四川省醫療器械檢測中心) "search.do?formAction=pqfGsByJG¶meter1=4028813a1d225be5011d226ba310001e", //湖北省藥品監督檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=4028813a1d225be5011d22697942001a", //吉林省藥品檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=4028813a1d225be5011d226392100002", //甘肅省藥品檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=4028813a1d225be5011d226c637d0020", //重慶市食品藥品檢驗檢測研究院 "search.do?formAction=pqfGsByJG¶meter1=20190917c001", //山東省食品藥品檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=20190924c001", //遼寧省藥品檢驗檢測院 "search.do?formAction=pqfGsByJG¶meter1=20210315c001", //雲南省食品藥品監督檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=20210926c001", //河北省藥品醫療器械檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=20211011c001", //浙江省食品藥品檢驗研究院 "search.do?formAction=pqfGsByJG¶meter1=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等工具連上隨意檢索。
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; } }