一、写在前面

  我写爬虫已经写了一段时间了,对于那些使用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来解析,我选择使用后者。示例代码如下:

 from lxml import etree

 html = '"<option  value=\"Autauga\">Autauga<\/option><option  value=\"Baldwin\">Baldwin<\/option><option  value=\"Barbour\">Barbour<\/option><option  value=\"Bibb\">Bibb<\/option><option  value=\"Blount\">Blount<\/option><option  value=\"Bullock\">Bullock<\/option><option  value=\"Butler\">Butler<\/option><option  value=\"Calhoun\">Calhoun<\/option><option  value=\"Chambers\">Chambers<\/option><option  value=\"Cherokee\">Cherokee<\/option><option  value=\"Chilton\">Chilton<\/option><option  value=\"Choctaw\">Choctaw<\/option><option  value=\"Clarke\">Clarke<\/option><option  value=\"Clay\">Clay<\/option><option  value=\"Cleburne\">Cleburne<\/option><option  value=\"Coffee\">Coffee<\/option><option  value=\"Colbert\">Colbert<\/option><option  value=\"Conecuh\">Conecuh<\/option><option  value=\"Coosa\">Coosa<\/option><option  value=\"Covington\">Covington<\/option><option  value=\"Crenshaw\">Crenshaw<\/option><option  value=\"Cullman\">Cullman<\/option><option  value=\"Dale\">Dale<\/option><option  value=\"Dallas\">Dallas<\/option><option  value=\"Dekalb\">Dekalb<\/option><option  value=\"Elmore\">Elmore<\/option><option  value=\"Escambia\">Escambia<\/option><option  value=\"Etowah\">Etowah<\/option><option  value=\"Fayette\">Fayette<\/option><option  value=\"Franklin\">Franklin<\/option><option  value=\"Geneva\">Geneva<\/option><option  value=\"Greene\">Greene<\/option><option  value=\"Hale\">Hale<\/option><option  value=\"Henry\">Henry<\/option><option  value=\"Houston\">Houston<\/option><option  value=\"Jackson\">Jackson<\/option><option  value=\"Jefferson\">Jefferson<\/option><option  value=\"Lamar\">Lamar<\/option><option  value=\"Lauderdale\">Lauderdale<\/option><option  value=\"Lawrence\">Lawrence<\/option><option  value=\"Lee\">Lee<\/option><option  value=\"Limestone\">Limestone<\/option><option  value=\"Lowndes\">Lowndes<\/option><option  value=\"Macon\">Macon<\/option><option  value=\"Madison\">Madison<\/option><option  value=\"Marengo\">Marengo<\/option><option  value=\"Marion\">Marion<\/option><option  value=\"Marshall\">Marshall<\/option><option  value=\"Mobile\">Mobile<\/option><option  value=\"Monroe\">Monroe<\/option><option  value=\"Montgomery\">Montgomery<\/option><option  value=\"Morgan\">Morgan<\/option><option  value=\"Perry\">Perry<\/option><option  value=\"Pickens\">Pickens<\/option><option  value=\"Pike\">Pike<\/option><option  value=\"Randolph\">Randolph<\/option><option  value=\"Russell\">Russell<\/option><option  value=\"Shelby\">Shelby<\/option><option  value=\"St Clair\">St Clair<\/option><option  value=\"Sumter\">Sumter<\/option><option  value=\"Talladega\">Talladega<\/option><option  value=\"Tallapoosa\">Tallapoosa<\/option><option  value=\"Tuscaloosa\">Tuscaloosa<\/option><option  value=\"Walker\">Walker<\/option><option  value=\"Washington\">Washington<\/option><option  value=\"Wilcox\">Wilcox<\/option><option  value=\"Winston\">Winston<\/option>"'
et = etree.HTML(html)
result = et.xpath('//option/text()')
result = [i.rstrip('"') for i in result]
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,使用示例如下:

 from itertools import product

 a = [1, 2, 3]
b = [2, 4]
result = product(a, b)
for i in result:
print(i, end=" ") # (1, 2) (1, 4) (2, 2) (2, 4) (3, 2) (3, 4)

  下面是农田数据的部分截图,其中包含了很多种类的作物,还有对应的耕地面积信息,不过在这个表中有些我们不需要的信息,比如耕地面积总量信息,还有空白行,这都是干扰数据,在解析的时候要清洗掉。

  解析农田数据部分的代码如下:

 et = etree.HTML(html)
crop_list = et.xpath('//*[@id="crm_results_eight"]/tbody/tr/td[1]/text()') # 作物名称
area_list = et.xpath('//*[@id="crm_results_eight"]/tbody/tr/td[2]/text()') # 耕地面积
conservation_list = et.xpath('//*[@id="crm_results_eight"]/tbody/tr/td[6]/text()') # 受保护耕地面积
crop_list = crop_list[:-3]
area_list = area_list[:-3]
conservation_list = conservation_list[:-3]

完整代码已上传到GitHub

【Python3爬虫】当爬虫碰到表单提交,有点意思的更多相关文章

  1. 结合API Gateway和Lambda实现登录时的重定向和表单提交请求(Python3实现)

    1. 创建Lambda函数,代码如下: from urllib import parse def lambda_handler(event, context): body = event['body' ...

  2. 用python模拟登录(解析cookie + 解析html + 表单提交 + 验证码识别 + excel读写 + 发送邮件)

    老婆大人每个月都要上一个网站上去查数据,然后做报表. 为了减轻老婆大人的工作压力,所以我决定做个小程序,减轻我老婆的工作量. 准备工作 1.tesseract-ocr 这个工具用来识别验证码,非常好用 ...

  3. scrapy formRequest 表单提交

    scrapy.FormRequest 主要用于提交表单数据 先来看一下源码 参数: formdata  (dict or iterable of tuples) – is a dictionary ( ...

  4. 阻止form表单提交的问题

    阻止form表单提交这种场景可能在生活中,我们经常碰到,而在我们第一印象里面可能我们用return false 去阻止表单默认行为. 但是,有中情况我们用return false 不能阻止表单提交 & ...

  5. CodeIgniter典型的表单提交验证代码

    view内容: <?php echo form_open('user/reg'); ?> <h5>用户名</h5> <input type="tex ...

  6. form表单提交数据编码方式和tomcat接受数据解码方式的思考

    http://blog.sina.com.cn/s/blog_95c8f1ac010198j2.html *********************************************** ...

  7. Django框架之第二篇--app注册、静态文件配置、form表单提交、pycharm连接数据库、django使用mysql数据库、表字段的增删改查、表数据的增删改查

    本节知识点大致为:静态文件配置.form表单提交数据后端如何获取.request方法.pycharm连接数据库,django使用mysql数据库.表字段的增删改查.表数据的增删改查 一.创建app,创 ...

  8. from表单提交数据之后,后台对象接受不到值

    如果SSH框架下,前段页面通过from表单提交数据之后,在后台对象显示空值,也就是接收不到值得情况下.首先保证前段输入框有值,这个可以在提交的时候用jQuery的id或者name选择器alert弹出测 ...

  9. 不使用Ajax,如何实现表单提交不刷新页面

    不使用Ajax,如何实现表单提交不刷新页面? 目前,我想到的是使用<iframe>,如果有其他的方式,后续再补. 举个栗子: 在表单上传文件的时候必须设置enctype="mul ...

随机推荐

  1. 【查虫日志】快速判断一副灰度图像中是否只有黑色和白色值(即是否为二值图像)过程中bool变量的是是非非。

    二值图像我们在图像处理过程中是经常遇到的,有的时候我们在进行一个算法处理前,需要判断下一副图像的数据是否符合二值图的需求,这个时候我们可以写个简单的函数来做个判断,比如我写了一个很简单的的代码如下: ...

  2. 编解码器之战:AV1、HEVC、VP9和VVC

    视频Codec专家Jan Ozer在Streaming Media West上主持了一场开放论坛,邀请百余名观众参与热门Codec的各项优势与短板.本文整理了讨论的主要成果,基本代表了AV1.HEVC ...

  3. auth-booster配置和使用(yii1.5)

    auth-booster这个是一个yii框架扩展中的一个模块.是非常好用的(但是里面的说明都是英文的,所以国人用还需要改一点里面的汉化) 1.下载auth-booster这个:http://www.y ...

  4. iOS自动化探索(十)代码覆盖率统计

    iOS APP代码覆盖率统计 今年Q3季度领导给加了个任务要做前后端代码覆盖率统计, 鉴于对iOS代码代码比较熟就选择先从iOS端入手,折腾一整天后终于初步把流程跑通了记录如下 覆盖率监测的原理 Xc ...

  5. 零基础ASP.NET Core MVC插件式开发

    零基础ASP.NET Core MVC插件式开发 一个项目随着业务模块的不断增加,系统会越来越庞大.如果参与开发的人员越多,管理起来也难度也很大.面对这样的情况,首先想到的是模块化插件式开发,根据业务 ...

  6. springboot2.x纯注解整合dubbo

    springboot1.x和springboot2.x整合差距挺大的,基于最新的2.x进行整合,使用纯注解的方式 依赖选取 首先pom文件的依赖引入,maven仓库有Apache和alibaba两个 ...

  7. 跟我学SpringCloud | 第十四篇:Spring Cloud Gateway高级应用

    SpringCloud系列教程 | 第十四篇:Spring Cloud Gateway高级应用 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 ...

  8. Linux命令学习-ls命令

    Linux中,ls命令的全称是list,主要作用是列出当前目录下的清单. 列出Linux根目录下的所有目录 ls / 列出当前目录下所有文件夹和文件 ls 列出当前目录下所有文件夹和文件(包括以&qu ...

  9. vmware的卸载

    vmware出了点问题,在控制面板里或者是360都没法删除干净.在网上搜了点资料,找到一些删除的方法,参考链接如下: http://zhidao.baidu.com/question/30902992 ...

  10. CAD2014学习笔记-文字编辑与尺寸标注

    基于 虎课网huke88.com CAD教程 文字与表格 输入文字:TEXT.MTEXT 插入表格:table 新建表格样式 尺寸标注 测量工具:Di.DLI 开启标注:打开工具-工具栏-标注 对齐/ ...