实现目标:采用爬虫的方式,通过查询名称批量化获取经纬度信息
前置条件:
1.python的环境搭建
python官网:https://www.python.org/downloads/
点击Python 3.11.8
下载好后直接点击运行安装
注意事项:
1)选择自定义安装
2)勾选add python to path
3)勾选install pip
2.VScode安装
3.第三方库下载
在vscode终端中输入
pip install playwright
playwright install
pip install pandas
pip install openpyxl
实现步骤:
1)建立一个文件夹(名为gis),其中共有四个文件(baidu.py、convers.py、input.xlsx、output_bd.xlsx)
2)baidu.py
# 导入
from playwright.sync_api import Playwright, sync_playwright
import pandas as pd
import convers as llc
import os
# 导入 定时器
# from time import sleep
result = pd.DataFrame() # 初始化
count = 0
print("---------------------------------------------------------------")
# 创建浏览器
def run (playwright: Playwright) -> None:
# 创建浏览器
browser = playwright.chromium.launch(headless=False)
# 使用 selenium 如果要打开多个网页,需要创建多个浏览器,但是 playwright 中只需要创建多个上下文即可
# 例如:content1 = browser.new_context()、content2 = browser.new_context() 分别去访问网页做处理
content = browser.new_context()
# 每个 content 就是一个会话窗口,可以创建自己的页面,也就是浏览器上的 tab 栏,在每个会话窗口中,可以创建多个页面,也就是多个 tab 栏
# 例如:page1 = content.new_page()、page2 = content.new_page() 封面去访问页面
page = content.new_page()
amp_api_data = pd.read_excel(r'input.xlsx')
# 页面打开指定网址
page.goto('https://api.map.baidu.com/lbsapi/getpoint/index.html')
def get_poi_from_amap(amp):
global result
global count
# 找到百度输入框( locator 会自动识别传入的选择器是 css xpath .... 不需要像 selenium 指定 By.XPATH/ID 这样的 )
page.fill('//*[@id="localvalue"]', amp[1])
# 点击百度地图地址输入框进行搜索
# page.locator('//input[@id="su"]').click() # 也可以写成下面这样:
page.click('//*[@id="localsearch"]')
page.wait_for_timeout(1000)
if page.locator('//*[@id="no_0"]/a').count() == 0:
page.wait_for_timeout(3000)
page.fill('//*[@id="localvalue"]', amp[0])
page.wait_for_timeout(1000)
page.click('//*[@id="localsearch"]')
page.wait_for_timeout(1000)
if page.locator('//*[@id="no_0"]/a').count() == 0:
busi_data = [
{
"input_name": amp[0],
"input_address": amp[1],
"output_name": 'Unknow',
"output_address": 'Unknow',
"bd09_lng": 'Unknow',
"bd09_lat": 'Unknow',
"wgs84_lng": 'Unknow',
"wgs84_lat": 'Unknow'
}
]
df = pd.DataFrame(busi_data)
result = pd.concat([result, df], axis=0, ignore_index=True)
count += 1
print(count,":",amp[0],"未能在地图中找到位置")
return
page.click('//*[@id="no_0"]/a')
page.wait_for_timeout(1000)
name = page.query_selector('//*[@id="no_0"]/a').text_content()
address = ((page.query_selector('//*[@id="no_0"]/p').text_content()).split(' ')[0]).split(':')[1]
lng = float(page.query_selector('//*[@id="pointInput"]').input_value().split(",")[0])
lat = float(page.query_selector('//*[@id="pointInput"]').input_value().split(",")[1])
wgs84_lng = round(llc.bd09_to_wgs84(lng,lat)[0],6)
wgs84_lat = round(llc.bd09_to_wgs84(lng,lat)[1],6)
busi_data = [
{
"input_name": amp[0],
"input_address": amp[1],
"output_name": name,
"output_address": address,
"bd09_lng": lng,
"bd09_lat": lat,
"wgs84_lng": wgs84_lng,
"wgs84_lat": wgs84_lat
}
]
df = pd.DataFrame(busi_data)
result = pd.concat([result, df], axis=0, ignore_index=True) # 将每次i结果union在一块(列方面追加)
# 延迟关闭(为啥需要延迟一下,这里是用于测试,因为代码执行完马上就回关闭,运行太快了,还以为崩溃了
# 暂时没找到配置不需要进行自动关闭,但是肯定跟 selenium 一样有这个配置)
# sleep(10) # 之前使用使用 sleep 的方式进行等待,传入的是单位是秒
# 但是在 playwright 中有自带的延迟等待,单位是毫秒
page.wait_for_timeout(3000)
# 遍历地址
for i in range(len(amp_api_data.values)):
get_poi_from_amap(amp_api_data.values[i])
print("进度:[",i+1,"/",len(amp_api_data),"]", sep='')
# 将脚本所在路径作为excel输出路径
output_path = os.getcwd() + os.sep + "output_bd.xlsx"
# 将结果写入到output_path 所在的excel中
result.to_excel(output_path, index=False)
print("---------------经纬度获取完成(来源:百度地图)-------------------")
print("有",count,"个地址未查询到对应的经纬度")
# 使用完成关闭上下文(也就是会话窗口)
content.close()
# 关闭浏览器
browser.close()
# 调用
with sync_playwright() as playwright:
run(playwright)
3)convers.py
"""
* 经纬度偏移转换
"""
import math
PI = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280
x_PI = float(PI * float(3000.0) / float(180.0))
aa = float(6378245.0)
ee = 0.00669342162296594323
def out_of_china(lng, lat):
# 纬度3.86~53.55,经度73.66~135.05
if 73.66 < lng < 135.05 and 3.86 < lat < 53.55:
return False
def transform_lat(lng, lat):
ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(lng))
ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 * math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lat * PI) + 40.0 * math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
ret += (160.0 * math.sin(lat / 12.0 * PI) + 320 * math.sin(lat * PI / 30.0)) * 2.0 / 3.0
return ret
def transform_lng(lng, lat):
ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(lng))
ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 * math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lng * PI) + 40.0 * math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
ret += (150.0 * math.sin(lng / 12.0 * PI) + 300.0 * math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
return ret
def bd09_to_gcj02(bd_lon, bd_lat):
x = bd_lon - 0.0065
y = bd_lat - 0.006
z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_PI)
theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_PI)
gg_lng = z * math.cos(theta)
gg_lat = z * math.sin(theta)
return [gg_lng, gg_lat]
def gcj02_to_wgs84(lng, lat):
# 判断是否为国外坐标
if out_of_china(lng, lat):
return [lng, lat]
else:
dlat = transform_lat(lng - 105.0, lat - 35.0)
dlng = transform_lng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((aa * (1 - ee)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (aa / sqrtmagic * math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [lng * 2 - mglng, lat * 2 - mglat]
def bd09_to_wgs84(lng, lat):
point = bd09_to_gcj02(lng, lat)
wgs84point = gcj02_to_wgs84(point[0], point[1])
return [wgs84point[0], wgs84point[1]]
4)input.xlsx
name:地点名称
address:地址
5)output_bd.xlsx
input_name:输入的地点名称
input_address:输入的地址
output_name:查询结果的地点名称
output_address:查询结果的地址
bd09_lng:查询结果的经度(来源baidu,有偏移)
bd09_lat:查询结果纬度(来源baidu,有偏移)
wgs84_lng:查询结果的经度(较为准确)
wgs84_lat:查询结果的纬度(较为准确)
6)所有文件准备好后,右击运行baidu.py
7)会弹出一个浏览器窗口,待查询结束后会关闭
8)可以在终端看到查询进度
问题汇总:
1)PermissionError: [Errno 13] Permission denied: 'E:\\gis\\output_bd.xlsx'
说明output_bd.xlsx文件未关闭,查询到的内容无法写入
2)开启无头浏览器
将baidu.py文件中的browser = playwright.chromium.launch(headless=False)修改为True