Python3 模拟登录并爬取表格数据!

本节主要内容有:

  • 通过requests库模拟表单提交
  • 通过pandas库提取网页表格

上周五,大师兄发给我一个网址,哭哭啼啼地求我:“去!把这个网页上所有年所有县所有作物的数据全爬下来,存到Access里!”

我看他可怜,勉为其难地挥挥手说:“好嘞,马上就开始!”

目标分析

Python学习交流群:1004391443

大师兄给我的网址是这个: https://www.ctic.org/crm?tdsourcetag=s_pctim_aiomsg

打开长这样:

 

 

 

 

根据我学爬虫并不久的经验,通常只要把年月日之类的参数附加到url里面去,然后用 requests.get 拿到 response 解析html就完了,所以这次应该也差不多——除了要先想办法获得具体有哪些年份、地名、作物名称,其他部分拿以前的代码稍微改改就能用了,毫无挑战性工作,生活真是太无聊了

点击 View Summary 后出现目标网页长这样

 

 

 

 

那个大表格的数据就是目标数据了,好像没什么了不起的——

有点不对劲

目标数据所在网页的网址是这样的: https://www.ctic.org/crm/?action=result ,刚刚选择的那些参数并没有作为url的参数啊!网址网页都变了,所以也不是ajax

这和我想象的情况有

巨大 差别啊

尝试获取目标页面

让我来康康点击 View Summary 这个按钮时到底发生了啥:右键 View Summary 检查是这样:

 

 

 

 

实话说,这是我第一次遇到要提交表单的活儿。以前可能是上天眷顾我,统统 get 就能搞定,今天终于让我碰上一个 post 了。

点击 View Summary ,到DevTools里找network第一条:

 

 

 

 

不管三七二十一, post 一下试试看

import requestsurl = 'https://www.ctic.org/crm?tdsourcetag=s_pctim_aiomsg'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ''AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/74.0.3729.131 Safari/537.36','Host': 'www.ctic.org'}
data = {'_csrf': 'SjFKLWxVVkkaSRBYQWYYCA1TMG8iYR8ReUYcSj04Jh4EBzIdBGwmLw==','CRMSearchForm[year]': '2011','CRMSearchForm[format]': 'Acres','CRMSearchForm[area]': 'County','CRMSearchForm[region]': 'Midwest','CRMSearchForm[state]': 'IL','CRMSearchForm[county]': 'Adams','CRMSearchForm[crop_type]': 'All','summary': 'county'}
response = requests.post(url, data=data, headers=headers)
print(response.status_code)

果不其然,输出 400 ……我猜这就是传说中的 cookies 在搞鬼吗?《Python3网络爬虫实战》只看到第6章的我不禁有些

心虚 跃跃欲试呢!

首先,我搞不清 cookies 具体是啥,只知道它是用来维持会话的,应该来自于第一次 get ,搞出来看看先:

response1 = requests.get(url, headers=headers)
if response1.status_code == 200:cookies = response1.cookiesprint(cookies)

输出:

<RequestsCookieJar[<Cookie PHPSESSID=52asgghnqsntitqd7c8dqesgh6 for www.ctic.org/>, <Cookie _csrf=2571c72a4ca9699915ea4037b967827150715252de98ea2173b162fa376bad33s%3A32%3A%22TAhjwgNo5ElZzV55k3DMeFoc5TWrEmXj%22%3B for www.ctic.org/>]>

Nah,看不懂,不看不管,直接把它放到 post 里试试

response2 = requests.post(url, data=data, headers=headers, cookies=cookies)
print(response2.status_code)

还是 400 ,气氛突然变得有些焦灼,我给你 cookies 了啊,你还想要啥?!

突然,我发现一件事: post 请求所带的 data 中那个一开始就显得很可疑的 _csrf 我仿佛在哪儿见过?

那个我完全看不懂的 cookies 里好像就有一个 _csrf 啊!但是两个 _csrf 的值很明显结构不一样,试了一下把 data 里的 _csrf 换成 cookies 里的 _csrf 确实也不行。

但是我逐渐有了一个想法:这个两个 _csrf 虽然不相等,但是应该是匹配的,我刚刚的 data 来自浏览器, cookies 来自python程序,所以不匹配!

于是我又点开浏览器的DevTools,Ctrl+F搜索了一下,嘿嘿,发现了:

 

 

 

 

 

 

这三处。

第一处那里的下一行的 csrf_token 很明显就是 post 请求所带的 data 里的 _csrf ,另外两个是js里的函数,虽然js没好好学但也能看出来这俩是通过 post 请求获得州名和县名的,Binggo!一下子解决两个问题。

为了验证我的猜想,我打算先直接用requests获取点击 View Summary 前的页面的HTML和 cookies ,将从HTML中提取的 csrf_token 值作为点击 View Summary 时 post 请求的 data 里的 _csrf 值,同时附上 cookies ,这样两处 _csrf 就应该是匹配的了:

from lxml import etree
response1 = requests.get(url, headers=headers)
cookies = response1.cookies
html = etree.HTML(response1.text)
csrf_token = html.xpath('/html/head/meta[3]/@content')[0]
data.update({'_csrf': csrf_token})
response2 = requests.post(url, data=data, headers=headers, cookies=cookies)
print(response2.status_code)

输出 200 ,虽然和Chrome显示的 302 不一样,但是也表示成功,那就不管了。把 response2.text 写入html文件打开看是这样:

 

 

 

 

Yeah,数据都在!说明我的猜想是对的!那一会再试试我从没用过的 requests.Session() 维持会话,自动处理 cookies 。

尝试pandas库提取网页表格

现在既然已经拿到了目标页面的HTML,那在获取所有年、地区、州名、县名之前,先测试一下 pandas.read_html 提取网页表格的功能。

pandas.read_html 这个函数时在写代码时IDE自动补全下拉列表里瞄到的,一直想试试来着,今天乘机拉出来溜溜:

import pandas as pd
df = pd.read_html(response2.text)[0]
print(df)

输出:

 

 

 

 

Yeah!拿到了,确实比自己手写提取方便,而且数值字符串自动转成数值,优秀!

准备所有参数

接下来要获取所有年、地区、州名、县名。年份和地区是写死在HTML里的,直接xpath获取:

 

 

 

 

州名、县名根据之前发现的两个js函数,要用 post 请求来获得,其中州名要根据地区名获取,县名要根据州名获取,套两层循环就行

def new():session = requests.Session()response = session.get(url=url, headers=headers)html = etree.HTML(response.text)return session, htmlsession, html = new()
years = html.xpath('//*[@id="crmsearchform-year"]/option/text()')
regions = html.xpath('//*[@id="crmsearchform-region"]/option/text()')
_csrf = html.xpath('/html/head/meta[3]/@content')[0]
region_state = {}
state_county = {}
for region in regions:data = {'region': region, '_csrf': _csrf}response = session.post(url_state, data=data)html = etree.HTML(response.json())region_state[region] = {x: y for x, y inzip(html.xpath('//option/@value'),html.xpath('//option/text()'))}for state in region_state[region]:data = {'state': state, '_csrf': _csrf}response = session.post(url_county, data=data)html = etree.HTML(response.json())state_county[state] = html.xpath('//option/@value')

啧啧,使用 requests.Session 就完全不需要自己管理 cookies 了,方便!具体获得的州名县名就不放出来了,实在太多了。然后把所有年、地区、州名、县名的可能组合先整理成csv文件,一会直接从csv里读取并构造 post 请求的 data 字典:

remain = [[str(year), str(region), str(state), str(county)] for year in years for region in regionsfor state in region_state[region] for county in state_county[state]]
remain = pd.DataFrame(remain, columns=['CRMSearchForm[year]','CRMSearchForm[region]','CRMSearchForm[state]','CRMSearchForm[county]'])
remain.to_csv('remain.csv', index=False)
# 由于州名有缩写和全称,也本地保存一份
import json
with open('region_state.json', 'w') as json_file:json.dump(region_state, json_file, indent=4)

我看了一下,一共49473行——也就是说至少要发送49473个 post 请求才能爬完全部数据,纯手工获取的话大概要点击十倍这个数字的次数……

正式开始

那么开始爬咯

import pyodbc
with open("region_state.json") as json_file:region_state = json.load(json_file)
data = pd.read_csv('remain.csv')
# 读取已经爬取的
cnxn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};''DBQ=./ctic_crm.accdb')
crsr = cnxn.cursor()
crsr.execute('select Year_, Region, State, County from ctic_crm')
done = crsr.fetchall()
done = [list(x) for x in done]
done = pd.DataFrame([list(x) for x in done], columns=['CRMSearchForm[year]','CRMSearchForm[region]','CRMSearchForm[state]','CRMSearchForm[county]'])
done['CRMSearchForm[year]'] = done['CRMSearchForm[year]'].astype('int64')
state2st = {y: x for z in region_state.values() for x, y in z.items()}
done['CRMSearchForm[state]'] = [state2st[x]for x in done['CRMSearchForm[state]']]
# 排除已经爬取的
remain = data.append(done)
remain = remain.drop_duplicates(keep=False)
total = len(remain)
print(f'{total} left.n')
del data# %%
remain['CRMSearchForm[year]'] = remain['CRMSearchForm[year]'].astype('str')
columns = ['Crop','Total_Planted_Acres','Conservation_Tillage_No_Till','Conservation_Tillage_Ridge_Till','Conservation_Tillage_Mulch_Till','Conservation_Tillage_Total','Other_Tillage_Practices_Reduced_Till15_30_Residue','Other_Tillage_Practices_Conventional_Till0_15_Residue']
fields = ['Year_', 'Units', 'Area', 'Region', 'State', 'County'] + columns
data = {'CRMSearchForm[format]': 'Acres','CRMSearchForm[area]': 'County','CRMSearchForm[crop_type]': 'All','summary': 'county'}
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ''AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/74.0.3729.131 Safari/537.36','Host': 'www.ctic.org','Upgrade-Insecure-Requests': '1','DNT': '1','Connection': 'keep-alive'}
url = 'https://www.ctic.org/crm?tdsourcetag=s_pctim_aiomsg'
headers2 = headers.copy()
headers2 = headers2.update({'Referer': url,'Origin': 'https://www.ctic.org'})
def new():session = requests.Session()response = session.get(url=url, headers=headers)html = etree.HTML(response.text)_csrf = html.xpath('/html/head/meta[3]/@content')[0]return session, _csrf
session, _csrf = new()
for _, row in remain.iterrows():temp = dict(row)data.update(temp)data.update({'_csrf': _csrf})while True:try:response = session.post(url, data=data, headers=headers2, timeout=15)breakexcept Exception as e:session.close()print(e)print('nSleep 30s.n')time.sleep(30)session, _csrf = new()data.update({'_csrf': _csrf})df = pd.read_html(response.text)[0].dropna(how='all')df.columns = columnsdf['Year_'] = int(temp['CRMSearchForm[year]'])df['Units'] = 'Acres'df['Area'] = 'County'df['Region'] = temp['CRMSearchForm[region]']df['State'] = region_state[temp['CRMSearchForm[region]']][temp['CRMSearchForm[state]']]df['County'] = temp['CRMSearchForm[county]']df = df.reindex(columns=fields)for record in df.itertuples(index=False):tuple_record = tuple(record)sql_insert = f'INSERT INTO ctic_crm VALUES {tuple_record}'sql_insert = sql_insert.replace(', nan,', ', null,')crsr.execute(sql_insert)crsr.commit()print(total, row.to_list())total -= 1
else:print('Done!')crsr.close()cnxn.close()

注意中间有个 try...except.. 语句,是因为不定时会发生 Connection aborted 的错误,有时9000次才断一次,有时一次就断,这也是我加上了 读取已经爬取的 和 排除已经爬取的 原因,而且担心被识别出爬虫,把 headers 写的丰富了一些(好像并没有什么卵用),并且每次断开都暂停个30s并重新开一个会话

 

 

 

 

然后把程序开着过了一个周末,命令行里终于打出了 Done! ,到Access里一看有816288条记录,心想:下次试试多线程(进程)和代理池。

周一,我把跑出来的数据发给大师兄,大师兄回我:“好的”。

隔着屏幕我都能感受到滔滔不绝的敬仰和感激之情,

一直到现在,大师兄都感动地说不出话来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/39497.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:用Python画龙:SEO技巧大放送

用Python画龙&#xff1a;SEO技巧大放送 Python是一种非常流行的编程语言&#xff0c;它可以用于编写软件、网站、数据分析和绘图等方面。而本文将为大家介绍如何在Python中使用Matplotlib库来画一条龙&#xff0c;并探讨如何通过SEO技巧来提高你的网站在搜索引擎排名中的权重…

chatgpt赋能Python-python3gui

Python3 GUI- 让你的应用程序更酷炫 随着技术的发展&#xff0c;图形用户界面(Graphical User Interface, GUI)已经成为软件开发过程中不可或缺的一部分。Python3是一个用于快速开发应用程序的强大编程语言&#xff0c;支持多种GUI库。本文将为您介绍Python3 GUI的一些基本概念…

chatgpt赋能python:Python滚动输出详解:如何实现动态的输出效果

Python滚动输出详解&#xff1a;如何实现动态的输出效果 在实际的Python编程中&#xff0c;经常需要将数据输出到控制台并对其进行展示&#xff0c;而滚动输出则是其中重要的一种方式。滚动输出能够让程序输出的信息实时地更新&#xff0c;提高可读性和用户体验。 什么是滚动…

想学前沿技术,苦于英语视频看不懂?有它就行了!

众所周知&#xff0c;作为一名程序员&#xff0c;如何快速实现职业生涯的快速发展甚至弯道超车&#xff0c;很多时候就看您对英文资料的掌握&#xff0c;比如&#xff1a;各种技术文档&#xff0c;大牛博客&#xff0c;英文书籍&#xff0c;还有视频。 如今国内火热的各种技术&…

GPT-4 IDEA神仙插件亲测帮助亿万用户解决痛点!

最近&#xff0c;Intellij IDEA的插件商店推出了一款新的插件——Bito&#xff0c;据说使用了GPT-4和ChatGPT来帮助开发人员编写代码&#xff0c;并且下载量已经达到了65K以上。 这款插件可以将GPT-4和ChatGPT引入IDE来大大提高开发人员的效率。它使用了OpenAI的模型&#xff0…

企业微信(二)——请在企业微信客户端打开链接

文章目录 目的问题现象一、PC端二、移动端总结 目的 总结在使用企业微信过程中碰到的问题&#xff0c;并记录解决办法。 问题现象 如下图&#xff0c;在接入企业微信接口进行免密登录功能实现的时候&#xff0c;访问企业微信的oauth2.0接口的时候出现如下提示 一、PC端 PC…

【企业微信实现免密登录以及发送消息(企业内部应用)】

创建应用&#xff1a; 首先是注册企业微信&#xff0c;没有企业信息个人也能注册&#xff0c;个人注册企业微信后无法认证&#xff0c;如果有开发第三方应用的需求就必须进行企业认证&#xff0c;如果只是开发企业内部应用&#xff0c;则无需注册也OK。 这里需要注意一下可见…

wechat_0010-企业微信,把自己的项目接入企业微信

1、一个可以公网访问的接入项目 2、需要一个企业微信账号 企业微信官网&#xff1a;https://work.weixin.qq.com/ 3、策略文件 报illegal key size异常时见 博客&#xff1a;http://www.cnblogs.com/shirui/p/7411735.html 4、微信的加密解密包 下载链接&#xff1a;http://…

我们用Windows官方跑了跑Linux GUI应用程序,不愧是“胶水操作系统”

梦晨 萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 现在&#xff0c;Windows不仅是最好的Linux发行版&#xff0c;还是最好的Linux桌面了。 怎么回事&#xff1f; 原来是Windows官方的Linux子系统&#xff08;WSL&#xff09;&#xff0c;终于支持Linux GUI了&#xff01; 也…

用了一下国产免费 AI 代码补全工具,真不错!

作者&#xff1a;去伪存真 原文&#xff1a;https://juejin.cn/post/7216992973418217533 前言 ChatGPT把AI带火了&#xff0c;我就在想&#xff0c;能用它做点什么呢&#xff0c;总不能只让它给我们带来焦虑&#xff0c;也要让我们从科技浪潮中分得一点红利吧。一番查找下来&a…

AI设计 | 如何让零基础小白在3分钟内利用AI人工智能设计Logo?

作为一个缺乏基础知识的初学者&#xff0c;你或许会认为Logo设计很难。但是&#xff0c;AI人工智能这个神奇的工具可以帮助你轻松地设计出自己的Logo&#xff0c;让整个过程变得愉快且简单。只要按照我的步骤并跟随我&#xff0c;你可以成为一位杰出的Logo设计师&#xff0c;不…

Tushare接口+LSTM模型预测股票走势

Tushare接口LSTM模型预测股票走势 Tushare ID&#xff1a;423115 Tushare接口优势以及使用方法 Tushare是一款国内使用较为热门的财经接口&#xff0c;数据源稳定不易出错&#xff0c;速度较快&#xff0c;能符合开发的需求&#xff0c;下面讲讲使用的基本方法。 注册账号 …

基于LSTM的对股票走势的预测

下图是基于LSTM的对股票走势的预测的结果 项目附件说明 本次实验项目文件有以下文件夹:stockPredict 文件夹。 1、根目录下存放使用的 python 代码。 2、data 文件夹下存放获取到的股票数据。 3、model 文件夹中包含已经训练好的模型。 4、img 文件夹中包含测试数据表现折线…

股票价、量走势图绘制

在证券投资分析领域中价、量走势分布图是投资者常用的一个参考方面。本案例主要介绍股票每日收盘价格、成交量的走势图以及月交易量分布饼图的绘制技能&#xff0c;并进一步介绍了子图的绘制方法 。 今有股票代码600000行情交易数据表&#xff08;trd.xlsx&#xff09;&#xf…

动态分析股票走势算法图,股票趋势预测算法

股票动态市盈率怎么计算出来的&#xff1f; 谈论起市盈率&#xff0c;这可真是有人爱&#xff0c;有人恨&#xff0c;有人认为有用&#xff0c;也认为无用。这个市盈率到底有没有用&#xff0c;咋用&#xff1f; 在为大家介绍我使用市盈率买股票的方法之前&#xff0c;先和大…

(个人简历一)用纯html写个人简历

效果 实现代码 用纯table实现 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>简历</title></head><body><table border"1" cellspacing"0" align"center"><t…

一位优秀前端工程师的简历应该怎么写?

​如今前端市场的从业人员很多且鱼龙混杂&#xff0c;真正的高手大牛非常稀缺&#xff0c;呈典型的金字塔形状&#xff0c;所以前端的招聘也是很多猎头及HR的痛点所在。那么在这种混乱的环境中&#xff0c;如何才能让自己突出重围&#xff1f;需要实力&#xff0c;更需要技巧。…

简历解析步骤(第一步)技术与实现(1)识文字,取信息

简历解析步骤&#xff08;第一步&#xff09;技术与实现&#xff08;1&#xff09;识文字&#xff0c;取信息 在上篇文章中&#xff0c;我们讲解了简历解析的理论。一般情况下&#xff0c;我们会以图片或文档的形式收到简历&#xff0c;为了实现解析&#xff0c;首先需要将其中…

实战简历编写,打造硬核敲门砖

实战简历编写&#xff0c;打造硬核敲门砖 1.自我介绍2. 技能描述3.工作经历4.项目经历4.1 商城4.2 进销存 自我评价教育经历 1.自我介绍 简历开篇就是介绍自己&#xff0c;忌长篇大论&#xff0c;那么点时间&#xff0c;相信你自己作为面试官&#xff0c;也不想去看阅读理解 …

1小时学会不打代码制作一个网页精美简历(1)

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;蓝桥签约作者。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#xff0c;迷茫的你会找到答案。系列教程将会在…