python+requests+ 爬取官網雙色球開獎資料
python+requests+mysql 爬取官網雙色球開獎資料
分析網頁資料獲取方式
第一種查詢方式
第二種查詢方式
注意:連結直接點過去的話是看不到任何東西的,服務端應該設定了某種 Referrer Policy
這個坑困擾了我有一陣 剛入坑python爬蟲就遇到這種棘手的問題
我並沒有從豆瓣爬蟲開始練起 而是找了一個需要的資料的連結開始爬
雖然會遇到更多的坑 但與此同時 在解決這些問題的過程中 也能學到更多的東西
這個問題經過反覆對比請求頭 終於發現了問題:
請求頭中不帶referrer資訊的話 是拿不到任何資料的
這裡可以對比兩次請求的請求頭來看
圖1 是成功拿到資料的請求頭
圖2 無任何資料返回
先簡單拿到部分資料看一下
url= 'http://www.cwl.gov.cn/cwl_admin/kjxx/findDrawNotice?name=ssq&issueStart=2018001&issueEnd=2018003'
res = requests.get(url)
print(res.text)
這樣寫的話 對於某些 連結是有效的 開始什麼資料都拿不到 確實很讓人迷惑
先開始想 是不是沒有加user-agent???
空想無用 嘗試驗證一下 這裡在請求頭中 加上user-angent試試
程式碼如下
url= 'http://www.cwl.gov.cn/cwl_admin/kjxx/findDrawNotice?name=ssq&issueStart=2018001&issueEnd=2018003'
headers = {
'User - Agent': 'Mozilla / 5.0(Windows NT 10.0;WOW64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 70.0.3538.77 Safari / 537.36'
}
res = requests. get(url, headers=headers)
print(res.text)
還是拿不到 資料 那就不只是user-angent的問題了
就像上面說的 通過反覆對比請求頭 最後終於解決了問題
url= 'http://www.cwl.gov.cn/cwl_admin/kjxx/findDrawNotice?name=ssq&issueStart=2018001&issueEnd=2018003'
headers = {
'Referer': 'http://www.cwl.gov.cn/kjxx/ssq/kjgg/',
}
res = requests.get(url, headers=headers)
print(res.text)
再次嘗試 ,結果不需要加user-angent 也可以,最終正常拿到資料的程式碼如上
現在可以簡單拿到回傳的資料了
為了好分析 這裡先拿兩期資料下來分析一下
拿到的資料如下:
{
'state': 0,
'message': '查詢成功',
'pageCount': 1,
'countNum': 2,
'Tflag': 1,
'result': [{
'name': '雙色球',
'code': '2014004',
'detailsLink': '/c/2014-01-09/384956.shtml',
'videoLink': '',
'date': '2014-01-09(四)',
'week': '四',
'red': '01,04,19,22,24,25',
'blue': '15',
'blue2': '',
'sales': '373155800',
'poolmoney': '201488460',
'content': '北京1注,安徽1注,山東2注,廣東1注,共5注。',
'addmoney': '',
'addmoney2': '',
'msg': '',
'z2add': '',
'm2add': '',
'prizegrades': [{
'type': 1,
'typenum': '5',
'typemoney': '9580091'
}, {
'type': 2,
'typenum': '113',
'typemoney': '303988'
}, {
'type': 3,
'typenum': '1035',
'typemoney': '3000'
}, {
'type': 4,
'typenum': '56508',
'typemoney': '200'
}, {
'type': 5,
'typenum': '1166507',
'typemoney': '10'
}, {
'type': 6,
'typenum': '8454477',
'typemoney': '5'
}, {
'type': 7,
'typenum': '',
'typemoney': ''
}]
}, {
'name': '雙色球',
'code': '2014003',
'detailsLink': '/c/2014-01-07/384933.shtml',
'videoLink': '',
'date': '2014-01-07(二)',
'week': '二',
'red': '06,10,11,28,30,33',
'blue': '12',
'blue2': '',
'sales': '363993362',
'poolmoney': '169237320',
'content': '內蒙古1注,江蘇2注,福建1注,四川1注,貴州1注,共6注。',
'addmoney': '',
'addmoney2': '',
'msg': '',
'z2add': '',
'm2add': '',
'prizegrades': [{
'type': 1,
'typenum': '6',
'typemoney': '7804024'
}, {
'type': 2,
'typenum': '147',
'typemoney': '171674'
}, {
'type': 3,
'typenum': '1778',
'typemoney': '3000'
}, {
'type': 4,
'typenum': '85239',
'typemoney': '200'
}, {
'type': 5,
'typenum': '1536591',
'typemoney': '10'
}, {
'type': 6,
'typenum': '11297659',
'typemoney': '5'
}, {
'type': 7,
'typenum': '',
'typemoney': ''
}]
}]
}
這裡的資料是以json格式回傳的 , 這樣也免了從頁面中匹配需要的資料
只需要從json中拿到我們需要的資料就可以了, 這裡先分析一下回傳的資料
'state': 0,
'message': '查詢成功',
'pageCount': 1,
'countNum': 2,
'Tflag': 1,
'result': [{
state欄位 是查詢狀態欄位 ,0就代表返回的json中有資料
result欄位中就包含了我們需要的資料
result': [{
'name': '雙色球',
'code': '2014004',
'detailsLink': '/c/2014-01-09/384956.shtml',
'videoLink': '',
'date': '2014-01-09(四)',
'week': '四',
'red': '01,04,19,22,24,25',
'blue': '15',
'blue2': '',
'sales': '373155800',
'poolmoney': '201488460',
'content': '北京1注,安徽1注,山東2注,廣東1注,共5注。',
'addmoney': '',
'addmoney2': '',
'msg': '',
'z2add': '',
'm2add': '',
'prizegrades': [{
'type': 1,
'typenum': '5',
'typemoney': '9580091'
}, {
'type': 2,
'typenum': '113',
'typemoney': '303988'
}, {
'type': 3,
'typenum': '1035',
'typemoney': '3000'
}, {
'type': 4,
'typenum': '56508',
'typemoney': '200'
}, {
'type': 5,
'typenum': '1166507',
'typemoney': '10'
}, {
'type': 6,
'typenum': '8454477',
'typemoney': '5'
}, {
'type': 7,
'typenum': '',
'typemoney': ''
}]
prizegrades 欄位中是中獎的 等級、注數 以及獎金數額(content欄位是一等獎中獎者的分佈)
這裡還需要注意下 沒有第7等的獎項,所以這裡是空的,插入資料庫的時候要注意一下
{
'type': 7,
'typenum': '',
'typemoney': ''
}]
需要的資料有 紅、藍球號碼 、開獎時間、期號、賣出時間 、獎池金額以及中獎情況的明細資料
完整程式碼
分析完了 開始碼程式碼
資料庫採用的是mysql
import requests
import json
import time
import mysql.connector
class Lottery(object):
def __init__(self):
self.db = mysql.connector.connect(
host = 'localhost',
user = 'root',#你的mysql 使用者名稱
passwd = '',#你的mysql 密碼
database = 'lottery'
)
self.cursor = self.db.cursor()
self.baseUrl = "http://www.cwl.gov.cn/cwl_admin/kjxx/findDrawNotice?name=ssq"
self.headers = {
'Referer': 'http://www.cwl.gov.cn/kjxx/ssq/kjgg/',
}
self.session = requests.Session()
# 定義起止期號 以及資料間隔期數
self.lastIssue = 157
self.firstIssue = 1
self.page = 50
#傳入int 型別的期號 返回期號str 處理期號 返回三位期號
def getIssueStr( self,issue):
issue = int(issue)
isStr = ''
if issue == 0:
return '001'
if issue < 10:
isStr = '00' + str( issue )
if issue <100 and issue >= 10:
isStr = '0' + str(issue)
if issue >= 100 and issue <self.lastIssue:
isStr = str(issue)
if issue > self.lastIssue:
return str(self.lastIssue)
return isStr
#獲取目標url
def getUrl(self, year, startIssue, lastIssue):
endIssue = self.getIssueStr(lastIssue)
Url = self.baseUrl + '&issueStart='+ str(year) +\
self.getIssueStr(startIssue)+'&issueEnd='+ str(year) + endIssue
print("Url:", Url)
return Url
def getResponse(self, url):
response = requests.get(url,headers=self.headers)
if response.status_code != 200:
return 'error'
else:
return response
def run(self):
list = range(2013, 2015) # 生成年份 官網只有從13年開始的資料 注意range函式的邊界值
#這裡時間跨度三年左右不會有問題 結束年為19年 傳入2018的話 不會包括18年的資料
issueList = range(self.firstIssue, self.lastIssue, self.page + 1)#生成期號
data = ''
for year in list:
for issue in issueList:
data = self.getResponse( self.getUrl(year,issue,issue+self.page))
if data == 'error':
print("response error")
continue
else:
self.saveData(data)
time.sleep(5)
def saveData(self,response):
res = json.loads(response.text)
resultList = res['result']
state = res['state']
if int(state) != 0:
print("無資料")
return
for result in resultList:
code = result['code']
date = result['date']
redballs = result['red']
blueball = result['blue']
sales = result['sales']
poolmoney = result['poolmoney']
content = result['content']
prizegrades = result['prizegrades']#中獎資訊列表
for pri in prizegrades:
type = pri['type']
typenum = pri['typenum']
typemoney = pri['typemoney']
if int(type) == 7:#該欄位資料為空 沒有該獎項 跳過
continue
self.cursor.execute('insert into bingo (code,level,num,money) VALUES (%s,%s,%s,%s)',(code,int(type),int(typenum),typemoney))
self.cursor.execute('insert into base (code,redballs,blueball,date,sales,poolmoney,content) VALUES (%s,%s,%s,%s,%s,%s,%s)',(code,redballs
,blueball,date,int(sales),int(poolmoney),content))
self.db.commit()
if __name__ == '__main__':
lottery = Lottery()
lottery.run()
資料庫指令碼
/*
Navicat MySQL Data Transfer
Source Server : 127.0.0.1_3306
Source Server Version : 80011
Source Host : 127.0.0.1:3306
Source Database : lottery
Target Server Type : MYSQL
Target Server Version : 80011
File Encoding : 65001
Date: 2018-11-01 16:44:33
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for base
-- ----------------------------
DROP TABLE IF EXISTS `base`;
CREATE TABLE `base` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(50) NOT NULL,
`redballs` varchar(50) NOT NULL,
`blueball` varchar(5) NOT NULL,
`date` varchar(50) NOT NULL,
`sales` int(11) NOT NULL COMMENT '總銷售額',
`poolmoney` int(11) NOT NULL COMMENT '獎池',
`content` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=897 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Table structure for bingo
-- ----------------------------
DROP TABLE IF EXISTS `bingo`;
CREATE TABLE `bingo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
`level` tinyint(4) NOT NULL,
`num` int(11) NOT NULL DEFAULT '0',
`money` varchar(50) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5377 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
這裡還有個問題 請求次數過多的話 會被強制中斷連線 這裡先將年份跨度設定小一點
請求的次數就會少一點 分批來爬取
程式碼中有設定time.sleep()目的是解決爬取資料被中斷連線的情況 可能是時間間隔設定太小 ,並沒有起到相應的作用,可以去掉或者將時間設定長一點嘗試一下
初次入坑python爬蟲 , 如文章中有錯誤 請多指教