如何用爬蟲下載中國土地市場網的土地成交數據?
作為畢業狗想研究下土地出讓方面的信息,需要每一筆的土地出讓數據。想從中國土地市場網的土地成交結果公告(http://www.landchina.com/default.aspx?tabid=263ComName=default)中點擊每一筆土地
,在跳轉後的詳細頁面中下載「土地用途」 「成交價格」 「供地方式」 「項目位置」等信息,
由於共有100多萬筆土地成交信息,手動查找是不可能了,想問下能不能用爬蟲給下載下來?以及預計難度和耗費時間?跪謝各位。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
import time
import random
import sys
def get_post_data(url, headers):
# 訪問一次網頁,獲取post需要的信息
data = {
"TAB_QuerySubmitSortData": "",
"TAB_RowButtonActionControl": "",
}
try:
req = requests.get(url, headers=headers)
except Exception, e:
print "get baseurl failed, try again!", e
sys.exit(1)
try:
soup = BeautifulSoup(req.text, "html.parser")
TAB_QueryConditionItem = soup.find(
"input", id="TAB_QueryConditionItem270").get("value")
# print TAB_QueryConditionItem
data["TAB_QueryConditionItem"] = TAB_QueryConditionItem
TAB_QuerySortItemList = soup.find(
"input", id="TAB_QuerySort0").get("value")
# print TAB_QuerySortItemList
data["TAB_QuerySortItemList"] = TAB_QuerySortItemList
data["TAB_QuerySubmitOrderData"] = TAB_QuerySortItemList
__EVENTVALIDATION = soup.find(
"input", id="__EVENTVALIDATION").get("value")
# print __EVENTVALIDATION
data["__EVENTVALIDATION"] = __EVENTVALIDATION
__VIEWSTATE = soup.find("input", id="__VIEWSTATE").get("value")
# print __VIEWSTATE
data["__VIEWSTATE"] = __VIEWSTATE
except Exception, e:
print "get post data failed, try again!", e
sys.exit(1)
return data
def get_info(url, headers):
req = requests.get(url, headers=headers)
soup = BeautifulSoup(req.text, "html.parser")
items = soup.find(
"table", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1")
# 所需信息組成字典
info = {}
# 行政區
division = items.find(
"span", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r1_c2_ctrl").get_text().encode("utf-8")
info["XingZhengQu"] = division
# 項目位置
location = items.find(
"span", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r16_c2_ctrl").get_text().encode("utf-8")
info["XiangMuWeiZhi"] = location
# 面積(公頃)
square = items.find(
"span", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r2_c2_ctrl").get_text().encode("utf-8")
info["MianJi"] = square
# 土地用途
purpose = items.find(
"span", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r3_c2_ctrl").get_text().encode("utf-8")
info["TuDiYongTu"] = purpose
# 供地方式
source = items.find(
"span", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r3_c4_ctrl").get_text().encode("utf-8")
info["GongDiFangShi"] = source
# 成交價格(萬元)
price = items.find(
"span", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r20_c4_ctrl").get_text().encode("utf-8")
info["ChengJiaoJiaGe"] = price
# print info
# 用唯一值的電子監管號當key, 所需信息當value的字典
all_info = {}
Key_ID = items.find(
"span", id="mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r1_c4_ctrl").get_text().encode("utf-8")
all_info[Key_ID] = info
return all_info
def get_pages(baseurl, headers, post_data, date):
print "date", date
# 補全post data
post_data["TAB_QuerySubmitConditionData"] = post_data[
"TAB_QueryConditionItem"] + ":" + date
page = 1
while True:
print " page {0}".format(page)
# 休息一下,防止被網頁識別為爬蟲機器人
time.sleep(random.random() * 3)
post_data["TAB_QuerySubmitPagerData"] = str(page)
req = requests.post(baseurl, data=post_data, headers=headers)
# print req
soup = BeautifulSoup(req.text, "html.parser")
items = soup.find("table", id="TAB_contentTable").find_all(
"tr", onmouseover=True)
# print items
for item in items:
print item.find("td").get_text()
link = item.find("a")
if link:
print item.find("a").text
url = "http://www.landchina.com/" + item.find("a").get("href")
print get_info(url, headers)
else:
print "no content, this ten days over"
return
break
page += 1
if __name__ == "__main__":
# time.time()
baseurl = "http://www.landchina.com/default.aspx?tabid=263"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36",
"Host": "www.landchina.com"
}
post_data = (get_post_data(baseurl, headers))
date = "2015-11-21~2015-11-30"
get_pages(baseurl, headers, post_data, date)
demo大概這個樣子的,
1 : 這個頁面每次最多顯示20頁,需要每10天當查詢條件.我只做了一個10天的, 你可以用datatime庫遍歷所有的時間.
2 : 先用requests.get訪問一次網頁,獲取所需的post內容
3: 具體每個土地頁面返回一個用唯一數值的電子監管號當key, 所需數據當值的字典,
4: 數據我沒有存儲, 只是列印出來,你可以根據所需的方式存儲, 可以存到csv中, 存到mysql中.
不請自來,知乎首答,同為大四畢業狗
之前幫老師爬過這個信息,從1995年-2015年有170多萬條,算了下時間需要40多個小時才能爬完。我爬到2000年就沒有繼續爬了。當時寫代碼的時候剛學爬蟲,不懂原理,發現這個網頁點擊下一頁以及改變日期後,網址是不會變的,網址是不會變的,網址是不會變的Orz,對於新手來說根本不知道是為什麼。後來就去找辦法,學了點selenium,利用它來模擬瀏覽器操作,更改日期、點擊下一頁什麼的都可以實現了。好處是簡單粗暴,壞處是殺雞用牛刀,佔用了系統太多資源。再到後來,學會了一點抓包技術,知道了原來日期和換頁都是通過post請求的。今天下午就把程序修改了一下,用post代替了原來的selenium。廢話不說,上代碼了。
# -*- coding: gb18030 -*-
"landchina 爬起來!"
import requests
import csv
from bs4 import BeautifulSoup
import datetime
import re
import os
class Spider():
def __init__(self):
self.url="http://www.landchina.com/default.aspx?tabid=263"
#這是用post要提交的數據
self.postData={ "TAB_QueryConditionItem":"9f2c3acd-0256-4da2-a659-6949c4671a2a",
"TAB_QuerySortItemList":"282:False",
#日期
"TAB_QuerySubmitConditionData":"9f2c3acd-0256-4da2-a659-6949c4671a2a:",
"TAB_QuerySubmitOrderData":"282:False",
#第幾頁
"TAB_QuerySubmitPagerData":""}
self.rowName=[u"行政區",u"電子監管號",u"項目名稱",u"項目位置",u"面積(公頃)",u"土地來源",u"土地用途",u"供地方式",u"土地使用年限",u"行業分類",u"土地級別",u"成交價格(萬元)",u"土地使用權人",u"約定容積率下限",u"約定容積率上限",u"約定交地時間",u"約定開工時間",u"約定竣工時間",u"實際開工時間",u"實際竣工時間",u"批准單位",u"合同簽訂日期"]
#這是要抓取的數據,我把除了分期約定那四項以外的都抓取了
self.info=[
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r1_c2_ctrl",#0
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r1_c4_ctrl",#1
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r17_c2_ctrl",#2
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r16_c2_ctrl",#3
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r2_c2_ctrl",#4
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r2_c4_ctrl",#5
#這條信息是土地來源,抓取下來的是數字,它要經過換算得到土地來源,不重要,我就沒弄了
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r3_c2_ctrl",#6
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r3_c4_ctrl",#7
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r19_c2_ctrl", #8
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r19_c4_ctrl",#9
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r20_c2_ctrl",#10
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r20_c4_ctrl",#11
## "mainModuleContainer_1855_1856_ctl00_ctl00_p1_f3_r2_c1_0_ctrl",
## "mainModuleContainer_1855_1856_ctl00_ctl00_p1_f3_r2_c2_0_ctrl",
## "mainModuleContainer_1855_1856_ctl00_ctl00_p1_f3_r2_c3_0_ctrl",
## "mainModuleContainer_1855_1856_ctl00_ctl00_p1_f3_r2_c4_0_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r9_c2_ctrl",#12
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f2_r1_c2_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f2_r1_c4_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r21_c4_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r22_c2",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r22_c4_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r10_c2_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r10_c4_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r14_c2_ctrl",
"mainModuleContainer_1855_1856_ctl00_ctl00_p1_f1_r14_c4_ctrl"]
#第一步
def handleDate(self,year,month,day):
#返回日期數據
"return date format %Y-%m-%d"
date=datetime.date(year,month,day)
# print date.datetime.datetime.strftime("%Y-%m-%d")
return date #日期對象
def timeDelta(self,year,month):
#計算一個月有多少天
date=datetime.date(year,month,1)
try:
date2=datetime.date(date.year,date.month+1,date.day)
except:
date2=datetime.date(date.year+1,1,date.day)
dateDelta=(date2-date).days
return dateDelta
def getPageContent(self,pageNum,date):
#指定日期和頁數,打開對應網頁,獲取內容
postData=self.postData.copy()
#設置搜索日期
queryDate=date.strftime("%Y-%m-%d")+"~"+date.strftime("%Y-%m-%d")
postData["TAB_QuerySubmitConditionData"]+=queryDate
#設置頁數
postData["TAB_QuerySubmitPagerData"]=str(pageNum)
#請求網頁
r=requests.post(self.url,data=postData,timeout=30)
r.encoding="gb18030"
pageContent=r.text
# f=open("content.html","w")
# f.write(content.encode("gb18030"))
# f.close()
return pageContent
#第二步
def getAllNum(self,date):
#1無內容 2隻有1頁 3 1—200頁 4 200頁以上
firstContent=self.getPageContent(1,date)
if u"沒有檢索到相關數據" in firstContent:
print date,"have","0 page"
return 0
pattern=re.compile(u"&
result=re.search(pattern,firstContent)
if result==None:
print date,"have","1 page"
return 1
if int(result.group(1))&<=200: print date,"have",int(result.group(1)),"page" return int(result.group(1)) else: print date,"have","200 page" return 200 #第三步 def getLinks(self,pageNum,date): "get all links" pageContent=self.getPageContent(pageNum,date) links=[] pattern=re.compile(u"&",re.S) results=re.findall(pattern,pageContent) for result in results: links.append("http://www.landchina.com/default.aspx?tabid=386"+result) return links def getAllLinks(self,allNum,date): pageNum=1 allLinks=[] while pageNum&<=allNum: links=self.getLinks(pageNum,date) allLinks+=links print "scrapy link from page",pageNum,"/",allNum pageNum+=1 print date,"have",len(allLinks),"link" return allLinks #第四步 def getLinkContent(self,link): "open the link to get the linkContent" r=requests.get(link,timeout=30) r.encoding="gb18030" linkContent=r.text # f=open("linkContent.html","w") # f.write(linkContent.encode("gb18030")) # f.close() return linkContent def getInfo(self,linkContent): "get every item"s info" data=[] soup=BeautifulSoup(linkContent) for item in self.info: if soup.find(id=item)==None: s="" else: s=soup.find(id=item).string if s==None: s="" data.append(unicode(s.strip())) return data def saveInfo(self,data,date): fileName= "landchina/"+datetime.datetime.strftime(date,"%Y")+"/"+datetime.datetime.strftime(date,"%m")+"/"+datetime.datetime.strftime(date,"%d")+".csv" if os.path.exists(fileName): mode="ab" else: mode="wb" csvfile=file(fileName,mode) writer=csv.writer(csvfile) if mode=="wb": writer.writerow([name.encode("gb18030") for name in self.rowName]) writer.writerow([d.encode("gb18030") for d in data]) csvfile.close() def mkdir(self,date): #創建目錄 path = "landchina/"+datetime.datetime.strftime(date,"%Y")+"/"+datetime.datetime.strftime(date,"%m") isExists=os.path.exists(path) if not isExists: os.makedirs(path) def saveAllInfo(self,allLinks,date): for (i,link) in enumerate(allLinks): linkContent=data=None linkContent=self.getLinkContent(link) data=self.getInfo(linkContent) self.mkdir(date) self.saveInfo(data,date) print "save info from link",i+1,"/",len(allLinks)
以上就是主要代碼了。程序主要分為4部分:
第1部分:
handleDate(self,year,month,day)------&>date
date就是要爬取的日期,是日期格式的
第2部分:
getAllNum(self,date)------&>allNum
allNum就是指定日期下,總共有多少頁的土地信息
第3部分:
getAllLinks(self,allNum,date)------&>allLinks
allLinks就是指定日期下所有的土地信息具體鏈接
第4部分:
saveAllInfo(self,allLinks,date)
把指定日期下,所有土地信息鏈接里的內容保存下來
下面是運行代碼,我是按每個月的天數來爬取的,這樣出錯了比較好修改~# -*- coding: gb18030 -*-
"landChina 執行起來!"
import spider
s=spider.Spider()
#日期
year=2000
month=9
day=1
delta=s.timeDelta(year,month)
#一個月一個月的抓取
while day&<=delta:
#日期
date=s.handleDate(year,month,day)
#頁數
allNum=s.getAllNum(date)
#鏈接
allLinks=s.getAllLinks(allNum,date)
#信息
s.saveAllInfo(allLinks,date)
day+=1
print date,"KO!"
print date.strftime("%Y-%m"),"KO!"
運行中是這樣的:
2000-09-12 have 1 page
scrapy link from page 1 / 1
2000-09-12 have 3 link
save info from link 1 / 3
save info from link 2 / 3
save info from link 3 / 3
2000-09-12 KO!
2000-09-13 have 0 page
2000-09-13 have 0 link
2000-09-13 KO!
2000-09-14 have 1 page
scrapy link from page 1 / 1
2000-09-14 have 3 link
save info from link 1 / 3
save info from link 2 / 3
save info from link 3 / 3
2000-09-14 KO!
2000-09-15 have 1 page
scrapy link from page 1 / 1
2000-09-15 have 2 link
save info from link 1 / 2
save info from link 2 / 2
2000-09-15 KO!
存儲路徑和存儲格式是這樣的:
landchina/year/month/day.csv
保存下來的信息是這樣的(用的是csv格式的):
170多萬條數據好大,想用分散式爬蟲來爬,可是不會呀~嗯,就是這樣,希望可以幫到你~
最近因為一個朋友需要中國土地網數據,所以寫程序抓取。在抓取的過程中遇到兩個問題:
1)每次搜索最多只顯示6000條數據;
2)如果頻率過快會被安全狗攔截。
常見的抓取有兩種方法,第一種是模擬瀏覽器發送http請求,第二種是模擬用戶使用瀏覽器的行為。這裡我採用第二種方法,具體來說是用selenium+chromedriver模擬用戶使用瀏覽器瀏覽網頁的行為。這種方法有幾點好處,比如邏輯簡單和可以執行js腳本等。
對於之前第一個問題,我先按照區縣搜索然後再分頁抓取,這樣問題差不多解決了。如果某一個區縣的數據超過6000條,再增加其他的搜索條件細分就行。對於第二個問題,安全狗攔截後會給出一個頁面
這裡用selenium同樣可以模擬用戶點擊"點擊進入"按鈕,當然安全狗不一定會在你模擬點擊一次後就放行,所以這裡你的程序需要短暫休眠並且不斷嘗試直到安全狗放行。
至此,兩個問題都解決了,其實還有第三個問題,那就是速度問題。
關於速度問題我的解決方法是,手動分散式,即將區縣劃分好後,每個程序負責一些區縣,最後匯總就行。具體就是將程序打包成可執行文件jar,然後在多台機器上同時運行。
最後說下抓取的整體過程,我是先按照區縣一頁一頁的下載網頁,然後分析網頁將數據入庫。然後再根據每一條數據,動態構造url如http://www.landchina.com/default.aspx?tabid=386comname=defaultwmguid=75c72564-ffd9-426a-954b-8ac2df0903b7recorderguid=e04657e5-03a1-46bb-b45c-29f31d1ed4d3,進而抓取每一條數據的詳情。
附上數據截圖:
目前網站加強了反爬蟲功能,點擊網址後會彈出安全狗驗證界面
在沒有通過這個驗證前是無法進入爬蟲爬取頁面的,我的解決思路是利用selenium,同時下載chrome瀏覽器插件chromedriver進行定時的模擬瀏覽器點擊,這樣就可以進入內容界面,同時爬蟲也可以繼續順利下載你需要的數據:
import selenium,platform
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0
from selenium.webdriver.common.by import By
import os
import time
driverPath = "/usr/bin/chromedriver"
os.environ["webdriver.chrome.driver"] = driverPath
url = "http://www.landchina.com"
driver = webdriver.Chrome()#.get(url)
#driver.get(url)
wait = WebDriverWait(driver, 40)
driver.get(url)
while True:
try:
driver.get(url)
driver.find_element_by_css_selector("body &> div &> div:nth-child(2) &> input[type="button"]").click()
time.sleep(60)
except:
print "error"
#driver.quit()
time.sleep(60)
關於具體的爬蟲代碼上面已經介紹的很詳細了,我目前爬取到17年的8月份,分享下我之前爬取的土地數據,歡迎感興趣的研究或前來討論。
鏈接: https://pan.baidu.com/s/1pKY6Z7H 密碼: 6q7r
終於知道為什麼中國土地市場網總癱瘓了。。。讓我一個畢業n年財經專業的上班狗情何以堪!!!
前2天看的,198W條信息,,,累成狗,我爬下為196萬,這麼大的量,必須用資料庫,去重,節省請求數,
還有土地抵押,土地轉讓,也有各40萬和80W數據。爬的好累呀,一個欄位一個欄位的對比。
這個網慢的要好,什麼分布,多線程,代理ip池,通通沒用,就算是單線程,速度一快,網站必掛。
很多人想2-3天,就把人家幾百萬數據,還有法律數據,3000多萬的數據,想1周爬一來,有這種想法的,早點打消掉,有點常識好不好。細水長流才能長久,有時不是你慢,而是人家伺服器差。
sundiontheway/landchina-spider 親測,好用
這個結構化如此清晰的數據,要採集這個數據是很容易的。 通過多年的數據處理經驗,可以給你以下幾個建議:
1. 多線程
2. 防止封IP
3. 用Mongdb存儲大型非結構化數據
我抓過這個網站的結束合同,還是比較好抓的。抓完生成表格,注意的就是選擇欄的非同步地區等內容,需要對他的js下載下來隊形非同步請求。提交數據即可。請求的時候在他的主頁有一個id。好像是這麼個東西,去年做的,記不清了,我有源碼可以給你分享。用java寫的
這種靜態的頁面,很容易爬取,也很容易解析·~·
要想一勞永逸,請自己學習,別做伸手黨·~·
知乎有很多關於爬蟲的專欄和大牛回答,寫得都很好。
我是爬蟲小白,請教下,不是說不能爬取asp的頁面嗎?
詳細內容頁的地址是」default.aspx?tabid=386comname=defaultwmguid=75c725。。。「,網站是在default.aspx頁讀取資料庫顯示詳細信息,不是說讀不到資料庫里的數據嗎?
推薦閱讀:
※用python爬拉鉤網關於『數據分析』工作的信息為什麼都是空的?
※如何使用爬蟲獲取新加坡PSI信息?
※爬蟲是不是用 Node.js 更好?
※為什麼寫的爬蟲只能爬取一幅圖,而不能全部下載所有圖片?
※要怎麼樣的訓練才能在PAT甲級考到八九十分?