Python零基础,只是粗略看过一些Python语法视频,有不对或更好的写法欢迎评论区指出。废话不多说直接上代码。
一、项目目录
const.py constant.py 常量工具类。main.py 爬虫程序
二、代码
1.常量类
先说一下这个常量类的作用,一开始我的设想是想参考Java项目那样可以把常量单独放到一个文件中,数据库连接单独放到一个文件中,sql语句单独放到一个文件中。但是实际开发中发现代码量其实很小,没必要单独写。对于比较复杂的工程来说可能会有这种需求。这里只尝试了常量类的归类及注册,避免了当有很多常量出现时,不小心重复或者名字不统一的情况。
const.py
import sys
"""
该类定义了一个方法__setattr()__,和一个异常ConstError, ConstError类继承
自类TypeError. 通过调用类自带的字典__dict__, 判断定义的常量是否包含在字典
中。如果字典中包含此变量,将抛出异常,否则,给新创建的常量赋值。
最后两行代码的作用是把const类注册到sys.modules这个全局字典中。
"""
class _const:
# 自定义异常处理
class ConstError(PermissionError):
pass
class ConstCaseError(ConstError):
pass
# 重写 __setattr__() 方法
def __setattr__(self, name, value):
if name in self.__dict__: # 已包含该常量,不能二次赋值
raise self.ConstError("Can't change const{0}".format(name))
if not name.isupper(): # 所有的字母需要大写
raise self.ConstCaseError("const name{0} is not all uppercase".format(name))
self.__dict__[name] = value
# 将系统加载的模块列表中的 constant 替换为 _const() 实例
sys.modules[__name__] = _const()
constant.py
import const
'''
定义常量: 格式:const.大写英文字母
const.DEMO = 1
const.DEMO = 4 # 常量已存在,重复报错ConstError
conStant.demo = 1 # 常量名字未大写,报错ConstCaseError
'''
# 爬虫信息
const.URL = 'https://webapi.sporttery.cn/gateway/lottery/getHistoryPageListV1.qry?gameNo=85&provinceId=0&pageSize=1' \
'&isVerify=1&pageNo=1 '
const.HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/106.0.0.0 Safari/537.36'}
const.ENCODING_GBK = 'GBK'
const.ENCODING_UTF8 = 'utf-8'
# 数据库信息
const.HOST = 'xxx.xxx.xxx.xxx'
const.USER = 'root'
const.PWD = 'password'
const.PORT = 3306
const.DBNAME = 'dbName'
2.爬虫程序
import requests
import pymysql
import traceback
import re
from datetime import datetime
from constant import const
'''
静态页面抓取,找到固定的url,请求抓取网页,然后使用lxml的xpath解析数据
from lxml import etree
resp = requests.get(url, headers=header,params=params) #括号内参数可选
...
html = etree.HTML(resp.text)
nums = html.xpath('//div[@class="ball_box01"]/ul/li[@class="ball_red"]/text()')
遍历nums获取数据
动态页面抓取,通过开发者工具的Fetch/XHR找到正确的请求,请求获取json,转化为字典,遍历索引获取数据
resp = requests.get(url, headers=header,params=params) #括号内参数可选
...
dic = resp.json()
'''
# 1.请求网址,获取响应
resp = requests.get(const.URL, headers=const.HEADERS)
resp.encoding = const.ENCODING_UTF8
# 2.解析响应内容,获取需要的数据
dic = resp.json().get('value').get('list')[0]
'''彩票名称'''
lotteryGameName = dic.get('lotteryGameName')
'''开奖时间'''
lotteryDrawTime = dic.get('lotteryDrawTime')
'''开奖期数'''
lotteryDrawNum = dic.get('lotteryDrawNum')
'''开奖结果''' # re.sub()可传三个参数 第一个要替换的数据,第二个要替换成的数据,第三个是你需要改变的字符串
lotteryDrawResult = re.sub(' ', ',', dic.get('lotteryDrawResult'))
# str.split(sep, maxsplit) sep是分割符,不写表示所有的空字符,包括空格、换行(\n)、制表符(\t)等,maxsplit是分割次数。
lotteryDrawResult_lit = dic.get('lotteryDrawResult').split()
'''出球顺序'''
lotteryUnsortDrawresult = dic.get('lotteryUnsortDrawresult').replace(' ', ',').replace('+', ',')
# 3.连接数据库存储数据
now = datetime.now()
now = now.strftime("%Y-%m-%d %H:%M:%S")
sql = "INSERT INTO `hng`.`lottery_DrawResult`(`lotteryGameType`, `lotteryDrawTime`, `lotteryDrawNum`, " \
"`lotteryDrawResult`, `lotteryUnsortDrawresult`, `lotteryDrawNum_red1`, `lotteryDrawNum_red2`, " \
"`lotteryDrawNum_red3`, `lotteryDrawNum_red4`, `lotteryDrawNum_red5`, `lotteryDrawNum_blue1`, " \
"`lotteryDrawNum_blue2`, `createTime`) " \
"VALUES " \
"('%s','%s', '%s','%s', '%s', '%s','%s', '%s', '%s','%s', '%s', '%s', '%s')"
data = (
lotteryGameName, lotteryDrawTime, lotteryDrawNum, lotteryDrawResult, lotteryUnsortDrawresult,
lotteryDrawResult_lit[0], lotteryDrawResult_lit[1], lotteryDrawResult_lit[2], lotteryDrawResult_lit[3],
lotteryDrawResult_lit[4], lotteryDrawResult_lit[5], lotteryDrawResult_lit[6], now)
def main():
try:
conn = pymysql.connect(host=const.HOST, user=const.USER, password=const.PWD, port=const.PORT, db=const.DBNAME,
charset="utf8")
print("mysql数据库连接成功,当前数据库版本为: %s" % conn.get_server_info())
cmd = conn.cursor()
cmd.execute(sql % data)
conn.commit()
print("更新影响的数据行数: %s" % cmd.rowcount)
print("最后一次增长的ID: %s" % cmd.lastrowid)
except Exception:
print("处理异常: " + traceback.format_exc())
finally:
conn.close()
if __name__ == "__main__":
main()
说明:
关于代码的解释都直接在代码中用注释进行了说明,但后续在部署中发现,代码中虽然是注释的部分或者打印的部分,只有存在中文或者中文字符就可能报字符编码的问题,尤其是使用'''XXX'''这种方式进行注释的时候,还有print('XXX')输出的内容中,这种尽可能不要使用中文及中文字符。部署中报错的解决办法是在Python文件头部添加一行指定编码格式的代码,但并不一定可以成功解决,所以还是尽量不要使用中文,如果想注释或者打印,可以使用英文。
这里额外上一个代码 historyData.py 这个代码是我用来抓取历史的,和主程序的区别就是 主程序是每次只抓取最新的一条数据,该程序可通过URL中的参数指定抓取一批数据。这不是重点,重点是想说明关于里面的数据库的插入操作
cmd.execute(sql % data) 单次插入,data为元组cmd.executemany(sql, data_lit) 批量插入,data_lit为元组列表
historyData.py
import requests
import pymysql
import traceback
import re
from datetime import datetime
from constant import const
# 通过指定期数获取数据
url = 'https://webapi.sporttery.cn/gateway/lottery/getHistoryPageListV1.qry?gameNo=85&provinceId=0&pageSize=30' \
'&isVerify=1&pageNo=1&startTerm=22123&endTerm=22124'
# 1.请求网址,获取响应
resp = requests.get(url, headers=const.HEADERS)
resp.encoding = const.ENCODING_UTF8
# 2.解析响应内容,获取需要的数据
dic_lit = resp.json().get('value').get('list')
# 3.连接数据库存储数据
now = datetime.now()
now = now.strftime("%Y-%m-%d %H:%M:%S")
sql = "INSERT INTO `hng`.`lottery_DrawResult`(`lotteryGameType`, `lotteryDrawTime`, `lotteryDrawNum`, " \
"`lotteryDrawResult`, `lotteryUnsortDrawresult`, `lotteryDrawNum_red1`, `lotteryDrawNum_red2`, " \
"`lotteryDrawNum_red3`, `lotteryDrawNum_red4`, `lotteryDrawNum_red5`, `lotteryDrawNum_blue1`, " \
"`lotteryDrawNum_blue2`, `createTime`) " \
"VALUE " \
"(%s,%s, %s,%s, %s, %s,%s, %s, %s,%s, %s, %s, %s)"
data_lit = []
for dic in dic_lit:
'''彩票名称'''
lotteryGameName = dic.get('lotteryGameName')
'''开奖时间'''
lotteryDrawTime = dic.get('lotteryDrawTime')
'''开奖期数'''
lotteryDrawNum = dic.get('lotteryDrawNum')
'''开奖结果''' # re.sub()可传三个参数 第一个要替换的数据,第二个要替换成的数据,第三个是你需要改变的字符串
lotteryDrawResult = re.sub(' ', ',', dic.get('lotteryDrawResult'))
# str.split(sep, maxsplit) sep是分割符,不写表示所有的空字符,包括空格、换行(\n)、制表符(\t)等,maxsplit是分割次数。
lotteryDrawResult_lit = dic.get('lotteryDrawResult').split()
'''出球顺序'''
lotteryUnsortDrawresult = dic.get('lotteryUnsortDrawresult').replace(' ', ',').replace('+', ',')
data = (
lotteryGameName, lotteryDrawTime, lotteryDrawNum, lotteryDrawResult, lotteryUnsortDrawresult,
lotteryDrawResult_lit[0], lotteryDrawResult_lit[1], lotteryDrawResult_lit[2], lotteryDrawResult_lit[3],
lotteryDrawResult_lit[4], lotteryDrawResult_lit[5], lotteryDrawResult_lit[6], now)
data_lit.append(data)
print(data_lit)
def main():
try:
conn = pymysql.connect(host=const.HOST, user=const.USER, password=const.PWD, port=const.PORT, db=const.DBNAME,
charset="utf8")
print("mysql数据库连接成功,当前数据库版本为: %s" % conn.get_server_info())
cmd = conn.cursor()
cmd.executemany(sql, data_lit)
conn.commit()
print("更新影响的数据行数: %s" % cmd.rowcount)
print("最后一次增长的ID: %s" % cmd.lastrowid)
except Exception:
print("处理异常: " + traceback.format_exc())
finally:
conn.close()
if __name__ == "__main__":
main()