本地創建數據庫,將 excel 數據存儲到 city 表中,再取 | 湖北省 | 的所有地級市和縣、縣級市、區數據作為樣表數據記錄在樣表中。利用 python 的 xlrd 包,定義 process_data 包來存放操作 excel 數據,生成 sql 語句的類,定義 op_postgresql 包來存放數據庫的操作對象,定義各種方法
城市距離爬取 - 任務計划
本地創建數據庫,將 excel 數據存儲到 city 表中,再取 | 湖北省 | 的所有地級市和縣、縣級市、區數據作為樣表數據記錄在樣表中。
本地創建數據庫,將 excel 數據存儲到 city 表中,再取 | 湖北省 | 的所有地級市和縣、縣級市、區數據作為樣表數據記錄在樣表中。准備工作創建好 public/config.py 擴展包,到時候,利用 python 的 xlrd 包,定義 process_data 包來存放操作 excel 數據,生成 sql 語句的類,
定義 op_postgresql 包來存放數據庫的操作對象,定義各種方法
創建 crwler 包,來存放爬蟲的操作對象 -> 發現對方網站調用的地圖 api -> 更改為調用德地圖 api 的包 - 存放操作對象
創建 log 文件夾,存放數據庫操作的日志
創建 data 文件夾,存放初始 excel 數據
數據庫基本構造:
樣本數據表格式:
表名:sample_table
| name | column | data type | length | 分布 | fk | 必填域 | 備注 |
|---|---|---|---|---|---|---|---|
| 地域名 | address | text | TRUE | 地域名 | |||
| 地域類型 | ad_type | integer | TRUE | 0 - 為地級市;1 - 為縣、縣級市、區。 | |||
| 經緯度 | coordinates | text | TRUE | 地域名的經緯度 | |||
| ··· |
樣本 1-1 地點 route 表的格式
表名:sample_route
| name | column | data type | length | 分布 | fk | 必填域 | 備注 |
|---|---|---|---|---|---|---|---|
| 出發點 | origin | text | 出發點 | ||||
| 目的點 | destination | text | 目的點 | ||||
| 距離 | distance | integer | 距離 | ||||
| 路線 | route | text | 路線 | ||||
| ··· |
創建配置信息接口
方便存儲我們需要的特定變量和配置信息。
public/config.py
#!/usr/bin/python # -*- coding: utf-8 -*- #__author__: stray_camel import os,sys #當前package所在目錄的上級目錄 src_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
創建讀取 excel 數據的接口
利用 python 的 xlrd 包,定義 process_data 包來存放操作 excel 數據,生成 sql 語句的類
參考 github 源碼 readme 文檔
並沒有發現在 PyPI 上有 document,所以只能去 github 上找源碼了,xlrd 處理 excel 基礎 guide
import xlrd book = xlrd.open_workbook("myfile.xls") print("The number of worksheets is {0}".format(book.nsheets)) print("Worksheet name(s): {0}".format(book.sheet_names())) sh = book.sheet_by_index(0) print("{0} {1} {2}".format(sh.name, sh.nrows, sh.ncols)) print("Cell D30 is {0}".format(sh.cell_value(rowx=29, colx=3))) for rx in range(sh.nrows): print(sh.row(rx))
創建 process_data/excel2sql.py 擴展包,方便后面 import
獲取 excel 的數據構造 sql 語句,創建 city 表(湖北省)樣表
process_data/excel2sql.py
#!/usr/bin/python # -*- coding: utf-8 -*- #__author__: stray_camel import xlrd,sys,os,logging from public import config class Excel2Sql(object): def __init__( self, url:"str類型的文件路徑", sheet:"excel中的表單名"): self.f_name = url # 將excel中特定表單名數據存儲起來 self.sh_data = xlrd.open_workbook(self.f_name).sheet_by_name(sheet) self.rows = self.sh_data.nrows self.cols = self.sh_data.ncols
當我們生成這個 Excel2Sql 對象的時候,我們希望按照類似
excel_data = excel2sql.Excel2Sql("fiel_name","sheet_name")
的代碼形式來直接讀取 excel 文件並獲取某個表單的數據。所以在初始化對象的時候我們希望對其屬性進行賦值。
excel 表中,我們按照下面的形式進行存儲數據:
| 省 / 直轄市 | 地級市 | 縣、縣級市、區 |
|---|---|---|
| 北京市 | 北京市 | 東城區 |
| ... | ... | ... |
之后我們希望通過調用這個類(接口)地時候能夠訪問其中一個函數,只獲取某個省 / 或者直轄市的所有數據,類似湖北省,我們指向獲取奇中 103 個縣、區。
在類 Excel2Sql 中定義方法:
def init_SampleViaProvince_name( self, Province_name:"省名" ) ->"insert的數據,列表形式[('地域名1','1','經緯度'),('地域名2','1','經緯度')]": geo_app = Geo_mapInterface(config.geo_key) all_data = [self.sh_data.row_values(i) for i in range(self.rows)] cities_data=[[["".join(i),1],["".join(i[1:len(i)]),1]][i[0]==i[1]] for i in all_data if i[0] == Province_name] for i in cities_data: i.append(geo_app.get_coordinatesViaaddress("".join(i[0]))) # cities_data=[[["".join(i),1,'test1'],["".join(i[1:len(i)]),1,'test2']][i[0]==i[1]] for i in all_data if i[0] == Province_name] return cities_data
之后我們可以測試類的構造是否正確,或進行調試:
在文件末端編寫:
if __name__ == "__main__": test = Excel2Sql(config.src_path+"\\data\\2019最新全國城市省市縣區行政級別對照表(194).xls","全國城市省市縣區域列表") print(test.init_SampleViaProvince_name("北京市"))
測試結果:
(env) PS F:\覽眾數據> & f:/覽眾數據/env/Scripts/python.exe f:/覽眾數據/城市距離爬取/process_data/excel2sql.py [['北京市東城區', 1, '116.416357,39.928353'], ['北京市西城區', 1, '116.365868,39.912289'], ['北京市崇文區', 1, '116.416357,39.928353'], ['北京市宣武區', 1, '116.365868,39.912289'], ['北京市朝陽區', 1, '116.601144,39.948574'], ['北京市豐台區', 1, '116.287149,39.858427'], ['北京市石景山區', 1, '116.222982,39.906611'], ['北京市海淀區', 1, '116.329519,39.972134'], ['北京市門頭溝區', 1, '116.102009,39.940646'], ['北京市房山區', 1, '116.143267,39.749144'], ['北京市通州區', 1, '116.656435,39.909946'], ['北京市順義區', 1, '116.654561,40.130347'], ['北京市昌 平區', 1, '116.231204,40.220660'], ['北京市大興區', 1, '116.341014,39.784747'], ['北京市平谷區', 1, '117.121383,40.140701'], ['北京市懷柔區', 1, '116.642349,40.315704'], ['北京市密雲縣', 1, '116.843177,40.376834'], ['北京 市延慶縣', 1, '115.974848,40.456951']]
創建 OP 數據庫 postgresql(其他數據庫也都一樣啦~)接口
定義 op_postgresql 包來存放數據庫的操作對象,定義各種方法
數據庫的 curd 真的是從大二寫到大四。
訪問 postgresql 數據庫一般用的包:psycopg2
訪問官網
在這個操作文檔網站中,使用的思路已經很清楚的寫出來了 http://initd.org/psycopg/docs/usage.html
希望大小少在網上走彎路(少看一些翻譯過來的文檔)。。。
http://initd.org/psycopg/
模式還是一樣,調用 postgresql 的驅動 / 接口,設置參數登陸,訪問數據庫。設置光標,注入 sql 數據,fetch 返回值。
- 這里需要注意的幾點是,默認防 xss 注入,寫代碼時一般設置參數訪問。
- 注意生成日志文件,打印日志
具體過程不贅述,直接上代碼
op_postgresql/opsql.py:
#!/usr/bin/python # -*- coding: utf-8 -*- #__author__: stray_camel ''' 定義對mysql數據庫基本操作的封裝 1.數據插入 2.表的清空 3.查詢表的所有數據 ''' import logging import psycopg2 from public import config class OperationDbInterface(object): #定義初始化連接數據庫 def __init__(self, host_db : '數據庫服務主機' = 'localhost', user_db: '數據庫用戶名' = 'postgres', passwd_db: '數據庫密碼' = '1026shenyang', name_db: '數據庫名稱' = 'linezone', port_db: '端口號,整型數字'=5432): try: self.conn=psycopg2.connect(database=name_db, user=user_db, password=passwd_db, host=host_db, port=port_db)#創建數據庫鏈接 except psycopg2.Error as e: print("創建數據庫連接失敗|postgresql Error %d: %s" % (e.args[0], e.args[1])) logging.basicConfig(stream=open(config.src_path + '/log/syserror.log', encoding="utf-8", mode="a"), level = logging.DEBUG,format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') logger = logging.getLogger(__name__) logger.exception(e) self.cur=self.conn.cursor() #定義在樣本表中插入數據操作 def insert_sample_data(self, condition : "insert語句" = "insert into sample_data(address,ad_type,coordinates) values (%s,%s,%s)", params : "insert數據,列表形式[('地域名1','1','經緯度'),('地域名2','1','經緯度')]" = [('地域名1','1','經緯度'),('地域名2','1','經緯度')] ) -> "字典形式的批量插入數據結果" : try: self.cur.executemany(condition,params) self.conn.commit() result={'code':'0000','message':'執行批量插入操作成功','data':len(params)} logging.basicConfig(stream=open(config.src_path + '/log/syserror.log', encoding="utf-8", mode="a"), level = logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') logger = logging.getLogger(__name__) logger.info("在樣本表sample_data中插入數據{}條,操作:{}!".format(result['data'],result['message'])) except psycopg2.Error as e: self.conn.rollback() # 執行回滾操作 result={'code':'9999','message':'執行批量插入異常','data':[]} 

