練習使用Python post請求爬取數據,並用mySQL數據庫進行存儲

    我看了許多教程,以前練習過爬取百度百科和汽車之家,都沒有成功,可能功力不到,有些問題總看不出究竟是哪裏出了毛病。html

    我看得其中有一個教程是作post請求爬取的,我拖拖拉拉的作了許久,剛好爬到數據了,所以特意發個文記念一下。html5

    聲明一個在不少教程裏面都看到的內容,不要頻繁的、大批量的使用代碼去訪問別人的網站,服務器是一種很貴的資源。python

    步驟一:嘗試爬取數據mysql

# urllib_post.py
# -*- coding:utf-8 -*- 
from urllib.request import urlopen
from urllib.request import Request
from urllib import parse

req = Request("https://kuai.baidu.com/pc/schedule/schedulelist")

postData = parse.urlencode([
	("startcityid", 224),
	("arrivalcityid", 289),
	("startdatetime", 1489507200),
	("us", "pc"),
	("hmsr", ""),
	("hmmd", ""),
	("hmpl", ""),
	("hmkw", ""),
	("hmci", ""),
])
print(postData)
req.add_header("Referer","https://kuai.baidu.com/pc")
req.add_header("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36")
resp = urlopen(req, data=postData.encode(encoding='utf-8'))#bytes(postData, "utf-8")

print(resp.read().decode("utf-8"))

    個人版本是Python3.5,使用urllib庫請求網頁數據。聽說Python2.x版本的urllib庫是分爲urllib和urllib2的,urllib2是urllib的升級,可是兩者的方法卻不重合,常常一塊兒使用。sql

    在請求網頁內容的時候,使用了.add_header添加請求頭,假裝成瀏覽器進行請求,這樣不容易被爬取的網站拒絕。數據庫

    直接用瀏覽器請求網站在控制檯(F12→Network→Header)裏面看到的是下面這樣兒的:json

    

    這裏我有一個疑問,就是看到Request Method:GET我忽然不知道本身是作的get請求仍是post請求了。瀏覽器

    

    假裝瀏覽器須要用到的主要就是User-Agent。服務器

    

    這個是跟在請求連接後面的參數,具體該怎麼叫我忘了。網絡

    另外,在寫代碼的時候我還遇到一個麻煩,urlopen(req,data)的data參數顯示應該爲二進制編碼,我直接傳入string類型報錯。這裏有2中方法能夠解決:1.string.encode(encoding='utf-8'));2.bytes(string, "utf-8")。

    步驟二:使用BeautifulSoup處理數據並存儲

# urllib_post_20170327.py
# -*- coding:utf-8 -*- 

from urllib.request import urlopen
from urllib.request import Request
from urllib import parse
from bs4 import BeautifulSoup
import json

req = Request("https://kuai.baidu.com/pc/schedule/schedulelist")
# 給請求添加一些Heather頭部
req.add_header("Referer","https://kuai.baidu.com/pc")
req.add_header("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36")

def get_one(page=0,schedulelist=[]):
	# 初始化時刻表
	if page == 0:
		schedulelist=[]

	# 轉換post數據
	postData = parse.urlencode([
		("startcityid", 224),# 224 蘇州
		("arrivalcityid", 289),# 289 上海
		("startdatetime", 1490630400),# 時間,不知道這個時間網站是怎麼計算的
		("us", "pc"),
		("hmsr", ""),
		("hmmd", ""),
		("hmpl", ""),
		("hmkw", ""),
		("hmci", ""),
		("page", page)
	])
	# print(postData)

	resp = urlopen(req, data=postData.encode(encoding='utf-8'))#bytes(postData, "utf-8")
	# print(resp.read().decode("utf-8"))

	# 轉換成BeautifulSoup進行處理
	bs = BeautifulSoup(resp,"lxml")

	if bs.find("ul",{"class":"bus-ls"}) and len(bs.find("ul",{"class":"bus-ls"}).find_all("li",{"class":"clearfix"}))>0:
		bs_schedulelist = bs.find("ul",{"class":"bus-ls"}).find_all("li",{"class":"clearfix"})

		for item in bs_schedulelist:
			schedule = {}
			schedule["startTime"] = item.find("p",{"class":"start-time-text"}).get_text()
			schedule["startStation"] = item.find("p",{"class":"start-station"}).get_text()
			schedule["arriveStation"] = item.find("p",{"class":"arrive-station"}).get_text()
			schedule["busLevel"] = item.find("div",{"class":"bus-lv"}).get_text()
			schedule["ticketPrice"] = item.find("div",{"class":"ticket-price"}).get_text()

			schedulelist.append(schedule)

		print(len(schedulelist))
		# 遞歸
		get_one(page=page+1, schedulelist=schedulelist)
	else:
		# 當不知足條件時返回內容,跳出遞歸
		print("獲取完成")
		print(json.dumps(schedulelist))
		return
		
	return json.dumps(schedulelist)

# 直接運行當前文件時執行的內容
if __name__== "__main__":
	schedulelist=get_one()
	print(schedulelist)

	# 文件操做
	fs = open("schedulelist.txt", "w")
	fs.write(schedulelist)
	fs.close()

    Beautiful Soup 是一個能夠從HTML或XML文件中提取數據的Python庫.它可以經過你喜歡的轉換器實現慣用的文檔導航,查找,修改文檔的方式.Beautiful Soup會幫你節省數小時甚至數天的工做時間.

    下表列出了主要的解析器,以及它們的優缺點:(這個是從BeautifulSoup官方文檔粘過來的)

解析器 使用方法 優點 劣勢
Python標準庫 BeautifulSoup(markup, "html.parser")
  • Python的內置標準庫
  • 執行速度適中
  • 文檔容錯能力強
  • Python 2.7.3 or 3.2.2)前 的版本中文檔容錯能力差
lxml HTML 解析器 BeautifulSoup(markup, "lxml")
  • 速度快
  • 文檔容錯能力強
  • 須要安裝C語言庫
lxml XML 解析器

BeautifulSoup(markup, ["lxml", "xml"])

BeautifulSoup(markup, "xml")

  • 速度快
  • 惟一支持XML的解析器
  • 須要安裝C語言庫
html5lib BeautifulSoup(markup, "html5lib")
  • 最好的容錯性
  • 以瀏覽器的方式解析文檔
  • 生成HTML5格式的文檔
  • 速度慢
  • 不依賴外部擴展

    解析器使用pip install進行下載。

    我使用的是lxml HTML 解析器,緣由簡單粗暴,由於我使用BeautifulSoup解析html的時候沒有傳入解析器對應的參數,報錯了以後提示我能夠這麼用【BeautifulSoup(markup, "lxml")】。

    我在處理分頁的時候用了遞歸,其實並不用這麼作,循環多是更好的方式。對於初識遞歸光環的我,好不容易能找個地方用一下,天然不會錯過。遞歸的時候也出了一點問題,就是我return返回的數據,本來個人return json.dumps(schedulelist)是寫在else裏面的,結果致使最外層的get_one()沒有返回任何數據,經大神提點,改爲了代碼如今的樣子。不過我內心仍是以爲本身寫的這個遞歸有點問題,至於哪裏有問題,可能還得更深刻的學習才知道。

    步驟三:使用數據庫存儲得到的數據

# -*- coding:utf-8 -*- 

from urllib.request import urlopen
from urllib.request import Request
from urllib import parse
from bs4 import BeautifulSoup
import json
import pymysql

req = Request("https://kuai.baidu.com/pc/schedule/schedulelist")
# 給請求添加一些Heather頭部
req.add_header("Referer","https://kuai.baidu.com/pc")
req.add_header("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36")

def get_one(page=0,schedulelist=[]):
	# 初始化時刻表
	if page == 0:
		schedulelist=[]

	# 轉換post數據
	postData = parse.urlencode([
		("startcityid", 224),# 224 上海
		("arrivalcityid", 289),# 289 蘇州
		("startdatetime", 1491062400),
		("us", "pc"),
		("hmsr", ""),
		("hmmd", ""),
		("hmpl", ""),
		("hmkw", ""),
		("hmci", ""),
		("page", page)
	])
	# print(postData)

	resp = urlopen(req, data=postData.encode(encoding='utf-8'))#bytes(postData, "utf-8")
	# print(resp.read().decode("utf-8"))

	# 轉換成BeautifulSoup進行處理
	bs = BeautifulSoup(resp,"lxml")

	if bs.find("ul",{"class":"bus-ls"}) and len(bs.find("ul",{"class":"bus-ls"}).find_all("li",{"class":"clearfix"}))>0:
		bs_schedulelist = bs.find("ul",{"class":"bus-ls"}).find_all("li",{"class":"clearfix"})

		for item in bs_schedulelist:
			schedule = {}
			schedule["startTime"] = item.find("p",{"class":"start-time-text"}).get_text()
			schedule["startStation"] = item.find("p",{"class":"start-station"}).get_text()
			schedule["arriveStation"] = item.find("p",{"class":"arrive-station"}).get_text()
			schedule["busLevel"] = item.find("div",{"class":"bus-lv"}).get_text()
			schedule["ticketPrice"] = item.find("div",{"class":"ticket-price"}).get_text()

			schedulelist.append(schedule)

		print(len(schedulelist))
		# 遞歸
		get_one(page=page+1, schedulelist=schedulelist)
	else:
		# 當不知足條件時返回內容,跳出遞歸
		print("獲取完成")
		return
		
	return schedulelist


class InsertSchedule(object):
	def __init__(self, conn):
		self.conn = conn

	def insert(self,schedulelist):
		cursor = self.conn.cursor()
		try:
			for schedule in schedulelist:
				sql = "insert schedule values(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")"%(schedule.get("startTime",None),schedule.get("startStation",None),schedule.get("arriveStation",None),schedule.get("busLevel",None),schedule.get("ticketPrice",None))
				print(sql)
				cursor.execute(sql)
		except Exception as e:
			print(e)
			raise e
		finally:
			cursor.close()

# 直接運行當前文件時執行的內容
if __name__== "__main__":
	conn = pymysql.connect(host="localhost",port=3306,user="root",password="qaz123456",db="demo",charset="utf8mb4")
	insertSchedule=InsertSchedule(conn)
	try:
		schedulelist=get_one()
		insertSchedule.insert(schedulelist);
		conn.commit()
	except Exception as e:
		conn.rollback()
		print("出現問題:"+str(e))
		raise e
	finally:
		conn.close()

    我定義了一個InsertSchedule類來進行數據庫操做,用到了一些關於事務提交和回滾的概念。實際上我這個簡單的demo是不須要這些東西的,可能寫在函數裏面會更簡單明瞭一些。可是鑑於我最近學習的內容,就直接生搬硬套的拿過來用的,這種用法在不少其餘的地方是很是有意思的。

    另外,聽說用循環插入多條數據是很是損耗數據庫性能的事情,能夠先得到插入多行的sql語句,再cursor.execute(sql)執行。固然,這樣的修改加上拼接字符串讓我感受很是難作,故老老實實作一個笨蛋初學者,將就如今這樣用了。

    還有一件很重要的事情,操做數據庫的數據鏈接對象connection和遊標對象cursor都應該及時關閉,以避免浪費資源。

    最終結果:

    過程十分綿長曲折,但最終結果讓人成功滿滿,清晰的數據呈如今眼前,彷彿打開了一扇新世界的大門。

 

參考資料:

  1. http://www.imooc.com/video/12625【使用urllib發送post請求(視頻)】
  2. http://www.ituring.com.cn/book/1709【《Python網絡數據採集》】
  3. https://www.crummy.com/software/BeautifulSoup/bs4/doc/【Beautiful Soup Documentation(有中文版可選擇,特別良心)】
  4. http://www.imooc.com/learn/475【Python操做MySQL數據庫(視頻)】
相關文章
相關標籤/搜索