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