1. 程式人生 > >python+requests+ 爬取官網雙色球開獎資料

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爬蟲 , 如文章中有錯誤 請多指教