??最近收到客服反應,系統(tǒng)的省市區(qū)數(shù)據(jù)好像不準,并且缺了一些地區(qū)。經(jīng)過詢問同事得知,數(shù)據(jù)庫內的數(shù)據(jù)是從老項目拷貝過來的,有些年頭了。難怪會缺一些數(shù)據(jù)。正好最近在對接網(wǎng)商銀行,發(fā)現(xiàn)網(wǎng)商提供了省市區(qū)的數(shù)據(jù)的接口。這就很舒服了哇,抄起鍵盤就是干,很快的就把同步程序寫好了。
??然后在同步的過程中,發(fā)現(xiàn)網(wǎng)商提供的數(shù)據(jù)和數(shù)據(jù)庫有些對不上。于是默默的打開淘寶和京東添加收貨地址,看看到底是誰錯了。對比到后面發(fā)現(xiàn)都有些差異。這就很蛋疼了??磥磉@個時候誰都不能相信了,只能信國家了。于是我打開了中華人民共和國民政部網(wǎng)站來比對異常的數(shù)據(jù)。
??對比的過程中,石錘網(wǎng)商數(shù)據(jù)不準。值得的是表揚淘寶和京東已經(jīng)同步了最新的數(shù)據(jù)了。但是呢,我并沒有找到它們的數(shù)據(jù)接口。為了修正系統(tǒng)的數(shù)據(jù),只能自己爬取了。
爬取地址如下:
https://preview.www.mca.gov.cn/article/sj/xzqh/2020/2020/202101041104.html
??爬取原理很簡單,就是解析HTML元素,然后獲取到相應的屬性值保存下來就好了。由于使用Java進行開發(fā),所以選用Jsoup來完成這個工作。
<!-- HTML解析器 -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
??由于需要解析HTML才能取到數(shù)據(jù),所以需要知道數(shù)據(jù)存儲在什么元素上。我們可以打開chrom的控制臺,然后選中對應的數(shù)據(jù),即可查看存儲數(shù)據(jù)的元素。
??通過分析,發(fā)現(xiàn)每一行數(shù)據(jù)都是存儲在一個<tr>標簽下。我們需要的 區(qū)域碼 和區(qū)域名稱存儲在第一和第二個<td>內 。與此同時還要很多空白<td>標簽,在編寫代碼是需要將其過濾掉。
??先定義好我們的爬取目標,以及Area實體類。
public class AreaSpider{
// 爬取目標
private static final String TARGET = "http://preview.www.mca.gov.cn/article/sj/xzqh/2020/2020/202101041104.html";
@Data
@AllArgsConstructor
private static class Area {
// 區(qū)域碼
private String code;
// 區(qū)域名稱
private String name;
// 父級
private String parent;
}
}
public static void main(String[] args) throws IOException{
// 請求網(wǎng)頁
Jsoup.connect(TARGET).timeout(10000).get()
// 篩選出 tr 標簽
.select("tr")
// 篩選出 tr 下的 td 標簽
.forEach(tr -> tr.select("td")
// 過濾 值為空的 td 標簽
.stream().filter(td -> StringUtils.isNotBlank(td.text()))
// 輸出結果
.forEach(td -> System.out.println(td.text())));
}
解析結果
??通過上面的代碼,我們已經(jīng)爬取到了頁面上的數(shù)據(jù)。但是并沒有達到我們的預期,所以進一步處理將其轉換為Area實體。
public static void main(String[] args) throws IOException{
// 請求網(wǎng)頁
List<Area> areaList = Jsoup.connect(TARGET).timeout(10000).get()
// 篩選出 tr 標簽
.select("tr")
// 篩選出 tr 下的 td 標簽
.stream().map(tr -> tr.select("td")
// 過濾 值為空的 td 標簽,并轉換為 td 列表
.stream().filter(td -> StringUtils.isNotBlank(td.text())).collect(Collectors.toList()))
// 前面提到,區(qū)域碼和區(qū)域名稱分別存儲在 第一和第二個td,所以過濾掉不符合規(guī)范的數(shù)據(jù)行。
.filter(e -> e.size() == 2)
// 轉換為 area 對象
.map(e -> new Area(e.get(0).text(), e.get(1).text(), "0")).collect(Collectors.toList());
// 遍歷數(shù)據(jù)
areaList.forEach(area -> System.out.println(JSONUtil.toJsonStr(area)));
}
解析結果
??至此,離我們想要的數(shù)據(jù)還差了父級區(qū)域碼 ,我們可以通過區(qū)域碼計算出來。以河北省為例:河北省:130000、石家莊市:130100、長安區(qū):130102可以發(fā)現(xiàn)規(guī)律:0000 結尾是省份,00是市。所以就有了如下代碼:
private static String calcParent(String areaCode){
// 省 - 針對第一行特殊處理
if(areaCode.contains("0000") || areaCode.equals("行政區(qū)劃代碼")){
return "0";
// 市
}else if (areaCode.contains("00")) {
return String.valueOf(Integer.parseInt(areaCode) / 10000 * 10000);
// 區(qū)
}else {
return String.valueOf(Integer.parseInt(areaCode) / 100 * 100);
}
}
public class AreaSpider{
// 爬取目標
private static final String TARGET = "http://preview.www.mca.gov.cn/article/sj/xzqh/2020/2020/202101041104.html";
@Data
@AllArgsConstructor
private static class Area{
// 區(qū)域碼
private String code;
// 區(qū)域名稱
private String name;
// 父級
private String parent;
}
public static void main(String[] args) throws IOException{
// 請求網(wǎng)頁
List<Area> areaList = Jsoup.connect(TARGET).timeout(10000).get()
// 篩選出 tr 標簽
.select("tr")
// 篩選出 tr 下的 td 標簽
.stream().map(tr -> tr.select("td")
// 過濾 值為空的 td 標簽,并轉換為 td 列表
.stream().filter(td -> StringUtils.isNotBlank(td.text())).collect(Collectors.toList()))
// 前面提到,區(qū)域碼和區(qū)域名稱分別存儲在 第一和第二個td,所以過濾掉不符合規(guī)范的數(shù)據(jù)行。
.filter(e -> e.size() == 2)
// 轉換為 area 對象
.map(e -> new Area(e.get(0).text(), e.get(1).text(), calcParent(e.get(0).text()))).collect(Collectors.toList());
// 去除 第一行 "行政區(qū)劃代碼|單位名稱"
areaList.remove(0);
areaList.forEach(area -> System.out.println(JSONUtil.toJsonStr(area)));
}
private static String calcParent(String areaCode){
// 省 - 針對第一行特殊處理
if(areaCode.contains("0000") || areaCode.equals("行政區(qū)劃代碼")){
return "0";
// 市
}else if (areaCode.contains("00")) {
return String.valueOf(Integer.parseInt(areaCode) / 10000 * 10000);
// 區(qū)
}else {
return String.valueOf(Integer.parseInt(areaCode) / 100 * 100);
}
}
}
??由于我們需要的是省市區(qū)三級數(shù)據(jù)聯(lián)動,但是了直轄市只有兩級,所以我們人工的給它加上一級。以北京市為例:變成了 北京 -> 北京市- >東城區(qū),對于其他的直轄市也是同樣的處理邏輯。
??修正好的數(shù)據(jù)奉上,有需要的小伙伴可以自取哦。
對于直轄市也可以做兩級的,這個主要看產(chǎn)品的需求吧
??總體來講,這個爬蟲比較簡單,只有簡單的幾行代碼。畢竟網(wǎng)站也沒啥反扒的機制,所以很輕松的就拿到了數(shù)據(jù)。
??嘿嘿話說,你都爬過哪些網(wǎng)站呢?
??如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,也可以隨手點個關注哦,謝謝。
??我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。我們下期見!
要求:寫一個省市區(qū)(或者年月日)的三級聯(lián)動,實現(xiàn)地區(qū)或時間的下拉選擇。
實現(xiàn)技術:php ajax
實現(xiàn):省級下拉變化時市下拉區(qū)下拉跟著變化,市級下拉變化時區(qū)下拉跟著變化。
使用chinastates表查詢
Ajax加載數(shù)據(jù)
1.這是chinastates表
2.做一個簡單php:Ajax_eg.php
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="bootstrap/js/jquery-1.11.2.min.js"></script>
</head>
<style>
.sanji{
margin-left: 550px;
margin-top: 150px;
}
</style>
<body>
<div class="sanji"> </div>
</body>
</html>
3.根據(jù)前一個頁面做jquery:Ajax_ssq.js
// JavaScript Document
//當頁面內容都加載完才執(zhí)行
$(document).ready(function(e) {
//加載三個下拉列表
$("#sanji").html("<select id='sheng'></select><select id='shi'></select><select id='qu'></select>");
//加載顯示數(shù)據(jù)
//1.加載省份
LoadSheng();
//2.加載市
LoadShi();
//3.加載區(qū)
LoadQu();
//當省份選中變化,重新加載市和區(qū)
$("#sheng").change(function(){ //當元素的值發(fā)生改變時,會發(fā)生 change 事件,該事件僅適用于文本域(text field),以及 textarea 和 select 元素。
//加載市
LoadShi();
//加載區(qū)
LoadQu();
})
//當市選中變化,重新加載區(qū)
$("#shi").change(function(){
//加載區(qū)
LoadQu();
})
});
//加載省份信息
function LoadSheng()
{
//取父級代號
var pcode ="0001";
//根據(jù)父級代號查數(shù)據(jù)
$.ajax({
//取消異步,也就是必須完成上面才能走下面
async:false,
url:"load.php",
data:{pcode:pcode},
type:"POST",
dataType:"JSON",
success: function(data){
var str="";
//遍歷數(shù)組,把它放入sj
for(var k in data){
str=str+"<option value='"+data[k].[0]+"'>"+data[k].[1]+"</option>";
}
$("#sheng").html(str);
}
});
}
//加載市信息
function LoadShi()
{
//取父級代號
var pcode =$("#sheng").val();
//根據(jù)父級代號查數(shù)據(jù)
$.ajax({
//取消異步,也就是必須完成上面才能走下面
async:false,
url:"load.php",
data:{pcode:pcode},
type:"POST",
dataType:"JSON",
success: function(data){
var str="";
//遍歷數(shù)組,把它放入sj
for(var k in data){
str=str+"<option value='"+data[k].[0]+"'>"+data[k].[0]+"</option>";
}
$("#shi").html(str);
}
});
}
//加載區(qū)信息
function LoadQu()
{
//取父級代號
var pcode =$("#shi").val();
//根據(jù)父級代號查數(shù)據(jù)
$.ajax({
//不需要取消異步
url:"load.php",
data:{pcode:pcode},
type:"POST",
dataType:"JSON",
success: function(data){
var str="";
//遍歷數(shù)組,把它放入sj
for(var k in data){
str=str+"<option value='"+data[k].[0]+"'>"+data[k].[1]+"</option>";
}
$("#qu").html(str);
}
});
}
4.再把數(shù)據(jù)庫連接起來 :load.php,把DBDA重新加載一個方法:JsonQuery
<?php
$pcode = $_POST["pcode"];
require_once "./DBDA.class.php";
$db = new DBDA();
$sql = "select * from chinastates where parentareacode='{$pcode}'";
echo $db->JsonQuery($sql,0);
<?php
class DBDA{
public $host="localhost";
public $uid="root";
public $pwd="";
public $dbname="0710_info";
/*
query方法:執(zhí)行用戶給的sql語句,并返回相應的結果
$sql:用戶需要執(zhí)行的sql語句
$type:用戶需要執(zhí)行的sql語句的類型
return:如果是增刪語句改返回true或false,如果是查詢語句返回二維數(shù)組
*/
public function query($sql,$type=1){//默認true為增刪改
$db = new MySQLi($this->host,$this->uid,$this->pwd,$this->dbname);
if(mysqli_connect_error()){
return "連接失敗!";
}
$result = $db->query($sql);
if($type==1){
return $result;//增刪改語句返回true或false
}else{
return $result->fetch_all();//查詢語句返回二維數(shù)組
}
}
//此方法用于ajax中用于對取出的數(shù)據(jù)(二維數(shù)組)進行拼接字符串處理
public function StrQuery($sql,$type=1){
$db = new MySQLi($this->host,$this->uid,$this->pwd,$this->dbname);
if(mysqli_connect_error()){
return "連接失敗!";
}
$result = $db->query($sql);
if($type==1){
return $result;//增刪改語句返回true或false
}else{
$arr = $result->fetch_all();//查詢語句返回二維數(shù)組
$str = "";
foreach($arr as $v){
$str = $str.implode("^", $v)."|";
}
$str = substr($str, 0,strlen($str)-1);
return $str;
}
}
//此方法用于ajax中用于返回為json數(shù)據(jù)類型時使用
public function JsonQuery($sql,$type=1){
$db = new MySQLi($this->host,$this->uid,$this->pwd,$this->dbname);
if(mysqli_connect_error()){
return "連接失敗!";
}
$result = $db->query($sql);
if($type==1){
return $result;//增刪改語句返回true或false
}else{
$arr = $result->fetch_all();//查詢語句返回二維(關聯(lián))數(shù)組
return json_encode($arr);//將數(shù)組轉換成json
}
}
}
實現(xiàn)效果:
ue實現(xiàn)城市三級聯(lián)動,可以通過使用Vue的v-model指令和computed屬性來實現(xiàn)。首先,需要準備一個包含省市區(qū)數(shù)據(jù)的JSON文件,例如:
```json
{
"provinces": [
{
"name": "北京市",
"cities": [
{
"name": "北京市",
"areas": [
"東城區(qū)",
"西城區(qū)",
"朝陽區(qū)",
"豐臺區(qū)",
"石景山區(qū)",
"海淀區(qū)",
"門頭溝區(qū)",
"房山區(qū)",
"通州區(qū)",
"順義區(qū)",
"昌平區(qū)",
"大興區(qū)",
"懷柔區(qū)",
"平谷區(qū)",
"密云區(qū)",
"延慶區(qū)"
]
}
]
},
{
"name": "上海市",
"cities": [
{
"name": "上海市",
"areas": [
"黃浦區(qū)",
"徐匯區(qū)",
"長寧區(qū)",
"靜安區(qū)",
"普陀區(qū)",
"虹口區(qū)",
"楊浦區(qū)",
"閔行區(qū)",
//省市區(qū)數(shù)據(jù)
const data = {
provinces: [
{
name: "北京市",
cities: [
{
name: "北京市",
areas: [
"東城區(qū)",
"西城區(qū)",
"朝陽區(qū)",
"豐臺區(qū)",
"石景山區(qū)",
"海淀區(qū)",
"門頭溝區(qū)",
"房山區(qū)",
"通州區(qū)",
"順義區(qū)",
"昌平區(qū)",
"大興區(qū)",
"懷柔區(qū)",
"平谷區(qū)",
"密云區(qū)",
"延慶區(qū)"
]
}
]
},
{
name: "上海市",
cities: [
{
name: "上海市",
areas: [
"黃浦區(qū)",
"徐匯區(qū)",
"長寧區(qū)",
"靜安區(qū)",
"普陀區(qū)",
"虹口區(qū)",
"楊浦區(qū)",
"閔行區(qū)"
]
}
]
},
{
name: "廣東省",
cities: [
{
name: "廣州市",
areas: [
"荔灣區(qū)",
"越秀區(qū)這是一個JSON文件,包含了省市區(qū)的數(shù)據(jù)。接下來,在Vue組件中使用這個數(shù)據(jù),實現(xiàn)城市三級聯(lián)動。
首先,需要在Vue組件中引入這個數(shù)據(jù)文件,并將其賦值給一個變量:
```javascript
import data from './data.json';
export default {
data() {
return {
provinces: data.provinces,
selectedProvince: '',
selectedCity: '',
selectedArea: ''
}
}
}
```
然后,在模板中使用v-model指令將選擇的省市區(qū)綁定到對應的變量上:
```html
<template>
<div>
<select v-model="selectedProvince">
<option value="">請選擇省份</option>
<option v-for="province in provinces" :value="province.name">{{ province.name }}</option>
</select>
<select v-model="selectedCity">
<option value="">請選擇城市</option>
<option v-for="city in selectedProvince.cities" :value="city.name">{{ city.name }}</option>
</select>
<select v-model="selectedArea">
<option value="">請選擇區(qū)域</option>
<option v-for="area in selectedCity.areas" :value="area">{{ area }}</option>
</select>
</div>
</template>
```
最后,使用computed屬性來動態(tài)獲取選擇的省市區(qū)的數(shù)據(jù):
```javascript
computed: {
selectedProvince() {
return this.provinces.find(province => province.name === this.selectedProvince);
},
selectedCity() {
if (this.selectedProvince) {
return this.selectedProvince.cities.find(city => city.name === this.selectedCity);
}
return null;
}
}
```
這樣,當選擇省份的時候,城市和區(qū)域的下拉框會根據(jù)選擇的省份動態(tài)更新。當選擇城市的時候,區(qū)域的下拉框會根據(jù)選擇的城市動態(tài)更新。最后,可以通過訪問`selectedProvince`、`selectedCity`和`selectedArea`來獲取選擇的省市區(qū)的數(shù)據(jù)。
*請認真填寫需求信息,我們會在24小時內與您取得聯(lián)系。