天天看點

【Python3爬蟲】當爬蟲碰到表單送出,有點意思

【Python3爬蟲】當爬蟲碰到表單送出,有點意思

一、寫在前面

  我寫爬蟲已經寫了一段時間了,對于那些使用GET請求或者POST請求的網頁,爬取的時候都還算得心應手。不過最近遇到了一個有趣的網站,雖然爬取的難度不大,不過因為表單送出的存在,是以一開始還是有點摸不着頭腦。至于最後怎麼解決的,請慢慢往下看。

二、頁面分析

  這次爬取的網站是:

https://www.ctic.org/crm?tdsourcetag=s_pctim_aiomsg

,該網站提供了美國的一些農田管理的資料。要檢視具體的資料,需要選擇年份、機關、地區、作物種類等,如下圖:

  根據以往的經驗,這種表單送出都是通過ajax來完成的,是以熟練地按F12打開開發者工具,選擇XHR選項,然後點選“View Summary”,結果卻什麼都沒有......

  這是怎麼回事?不急,切換到All看一下有沒有什麼可疑的東西。果然就找到了下面這個,可以看到在Form Data中包含了很多參數,而且可以很明顯看出來是一些年份、地區等資訊,這就是表單送出的内容:

  可以注意到在這些參數中有一個_csrf,很明顯是一個加密參數,那麼要怎麼得到這個參數呢?傳回填寫表單的網頁,在開發者工具中切換到Elements,然後搜尋_csrf看看,很快就找到了如下資訊:

  其餘參數都是表單中所選擇的内容,隻要對應填寫就行了。不過這個請求傳回的狀态碼是302,為什麼會是302呢?302狀态碼的使用場景是請求的資源暫時駐留在不同的URI下,是以還需要繼續尋找。

  通過進一步查找可知,最終的URL是:

https://www.ctic.org/crm/?action=result

  

三、主要步驟

1.爬取郡縣資訊

  可以看到表單中包含了地區、州、郡縣選項,在填寫表單的時候,這一部分都是通過JS來實作的。打開開發者工具,然後在頁面上點選County,選擇Region和State,就能在開發者工具中找到相應的請求。主要有兩個請求,如下:

https://www.ctic.org/admin/custom/crm/getstates/ https://www.ctic.org/admin/custom/crm/getcounties/

  這兩個請求傳回的結果格式如下圖:

  這裡可以使用正則比對,也可以使用lxml來解析,我選擇使用後者。示例代碼如下:

複制代碼

1 from lxml import etree

2

3

4 html = '"AutaugaBaldwinBarbourBibbBlountBullockButlerCalhounChambersCherokeeChiltonChoctawClarkeClayCleburneCoffeeColbertConecuhCoosaCovingtonCrenshawCullmanDaleDallasDekalbElmoreEscambiaEtowahFayetteFranklinGenevaGreeneHaleHenryHoustonJacksonJeffersonLamarLauderdaleLawrenceLeeLimestoneLowndesMaconMadisonMarengoMarionMarshallMobileMonroeMontgomeryMorganPerryPickensPikeRandolphRussellShelbySt ClairSumterTalladegaTallapoosaTuscaloosaWalkerWashingtonWilcoxWinston"'

5 et = etree.HTML(html)

6 result = et.xpath('//option/text()')

7 result = [i.rstrip('"') for i in result]

8 print(result)

  上面代碼輸出的結果為:

['Autauga', 'Baldwin', 'Barbour', 'Bibb', 'Blount', 'Bullock', 'Butler', 'Calhoun', 'Chambers', 'Cherokee', 'Chilton', 'Choctaw', 'Clarke', 'Clay', 'Cleburne', 'Coffee', 'Colbert', 'Conecuh', 'Coosa', 'Covington', 'Crenshaw', 'Cullman', 'Dale', 'Dallas', 'Dekalb', 'Elmore', 'Escambia', 'Etowah', 'Fayette', 'Franklin', 'Geneva', 'Greene', 'Hale', 'Henry', 'Houston', 'Jackson', 'Jefferson', 'Lamar', 'Lauderdale', 'Lawrence', 'Lee', 'Limestone', 'Lowndes', 'Macon', 'Madison', 'Marengo', 'Marion', 'Marshall', 'Mobile', 'Monroe', 'Montgomery', 'Morgan', 'Perry', 'Pickens', 'Pike', 'Randolph', 'Russell', 'Shelby', 'St Clair', 'Sumter', 'Talladega', 'Tallapoosa', 'Tuscaloosa', 'Walker', 'Washington', 'Wilcox', 'Winston']

  擷取所有郡縣資訊的思路為分别選擇四個地區,然後周遊每個地區下面的州,再周遊每個州所包含的郡縣,最終得到所有郡縣資訊。

2.爬取農田資料

  在得到郡縣資訊之後,就可以構造擷取農田資料的請求所需要的參數了。在擷取農田資料之前,需要向伺服器發送一個送出表單的請求,不然是得不到資料的。在我測試的時候,發送送出表單的請求的時候,傳回的狀态碼并不是302,不過這并不影響之後的操作,是以可以忽略掉。

  需要注意的是,參數中是有一個年份資訊的,前面我一直是預設用的2011,不過要爬取更多資訊的話,還需要改變這個年份資訊。而通過選擇頁面元素可以知道,這個網站提供了16個年份的農田資料資訊,這16個年份為:

[1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,2002,2004,2006,2007,2008,2011]

  得到這些年份資訊之後,就可以和前面的郡縣資訊進行排列組合得到所有送出表單的請求所需要的參數。說道排列組合,一般會用for循環來實作,不過這裡推薦一種方法,就是使用itertools.product,使用示例如下:

1 from itertools import product

3 a = [1, 2, 3]

4 b = [2, 4]

5 result = product(a, b)

6 for i in result:

7 print(i, end=" ")

8

9

10 # (1, 2) (1, 4) (2, 2) (2, 4) (3, 2) (3, 4)

  下面是農田資料的部分截圖,其中包含了很多種類的作物,還有對應的耕地面積資訊,不過在這個表中有些我們不需要的資訊,比如耕地面積總量資訊,還有空白行,這都是幹擾資料,在解析的時候要清洗掉。

  解析農田資料部分的代碼如下:

1 et = etree.HTML(html)

2 crop_list = et.xpath('//*[@id="crm_results_eight"]/tbody/tr/td[1]/text()') # 作物名稱

3 area_list = et.xpath('//*[@id="crm_results_eight"]/tbody/tr/td[2]/text()') # 耕地面積

4 conservation_list = et.xpath('//*[@id="crm_results_eight"]/tbody/tr/td[6]/text()') # 受保護耕地面積

5 crop_list = crop_list[:-3]

6 area_list = area_list[:-3]

7 conservation_list = conservation_list[:-3]

完整代碼已上傳到GitHub!

原文位址

https://www.cnblogs.com/TM0831/p/11273050.html