2021春项目需求记录 python实现模拟登录+爬取NASA Modis 上的产品数据
作者:互联网
python实现模拟登录+爬取Nasa Modis 上的产品数据
概述
3月的中旬时候参与了学校的一个大创项目,作为本科生,本人只是摸鱼打杂,负责了其中的一个功能模块:爬取NASA Modis数据。
整个过程也只是慢慢修改增添方案,主体功能于四月中旬基本完成。因为后续并未真正用到,所以自己还没进行更多优化,请各位谅解。如个人使用参考,可以试试这个爬虫。(2021年3-5月期间的,最近没留意网站更新)
整个过程中,可以收获对Cookie、python的selenium库以及基本爬虫知识的一定了解(也是我自己学到的一些东西)。
基本思路
代码
nasa_moids.py:
import requests
import json
import os
import random
import time
import datetime
from dateutil.relativedelta import relativedelta
import autoClick
__author__ = 'Ray'
'''
Task:输入产品信息,附加筛选条件,自动爬取Modis遥感数据
观察/记录:
1.数据一般延时更新,且所选区域在部分日期下没有数据
2.仅hdf所在URL需要cookies,所以应模拟登录获取Cookies(其实短期内Cookies不变,如果不要求过高,可以手动输入Cookies)
3.要用到中间URL,找规律
Tips:
1.网络事先需要连接好,并保持稳定,否则不只会影响模拟点击。(看到一种说法是借助浏览器厂商差异,换FireFox,还未尝试)
2.Modis网站的服务器运行状况良好
3.解决访问时间过长,模拟点击报错问题!(循环尝试?),下载URL卡死问题
4.目前的访问和下载速度较慢,考虑异步,多线程下载或其他方式
5.ban,代理池,访问频率(考虑要不要定期清除Cookie)
6.符合使用需求
7.后续维护问题,观察网站变化,也要靠考虑浏览器版本升级与驱动的兼容
'''
class Nasa: # 面向对象编程,创建类
def __init__(self):
self.headers1 = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36",
'Referer': 'https://ladsweb.modaps.eosdis.nasa.gov/search/order/4/MOD11A1--6/2021-02-22..2021-03-08/DB/97,30.1,107.1,21.2',
'Host': 'ladsweb.modaps.eosdis.nasa.gov',
'Accept': '*/*',
'Connection': 'keep-alive',
'X-Requested-With': 'XMLHttpRequest'}
# 还未修改Referer!!
# 留意下headers会不会变化
# 当遇到403时,headers的内容越全越有希望,除了在get()方法中添加cookies参数,也可以在headers字典中添加cookies
self.headers2 = {
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3"}
# 获取当前日期范围
def getDate(self, if_range='last month', range=20):
# example: '2021-02-22..2021-03-08'
date = time.strftime('%Y-%m-%d', time.localtime())
# 如果是范围筛选,还要考虑年份和月份的灵活变化
if if_range == 'all':
date = '2020-01-01..' + date
else:
date = str(datetime.date.today() + relativedelta(days=-30)) + '..' + str(date)
print('Date range:' + str(date) + '\n')
return date
# 获取页面
def getPage(self, url, cookie=None):
try:
re = requests.get(url, headers=self.headers1, timeout=(9.05, 6.05), cookies=cookie)
print('已完成get指令')
print(re.status_code)
re.raise_for_status()
re.encoding = re.apparent_encoding
print('获取页面成功!准备return')
# time.sleep(1)
return re
except requests.exceptions.RequestException as e:
print(e)
print('请求失败!您的网络状况可能存在一点问题,需重试')
# 将开发者工具界面中,Headers里显示的cookie转换为字典
def cookieToDict(self, cookie):
cookie = cookie.split(';')
cok = dict()
for i in cookie:
part = i.split('=')
part[0] = part[0].strip(' ')
cok[part[0]] = part[1]
return cok
# 通过间接爬取,下载数据文件
def getHdf(self, data_kind, date_ranges, area, cookie):
for kind in data_kind:
url1 = 'https://ladsweb.modaps.eosdis.nasa.gov/api/v1/files/product={}&collection={}&dateRanges={}&areaOfInterest={}&dayCoverage=true&dnboundCoverage=true'.format(
data_kind[kind][0], data_kind[kind][1], date_ranges, area)
url2_head = 'https://ladsweb.modaps.eosdis.nasa.gov'
# 获取hdf的URL字段
while True:
try:
re = nasa.getPage(url1)
# print('str:\n' + re.text[:500] + '\n') # 测试而已
text = json.loads(re.text, strict=False)
# text = re.json()
print(text)
print('\n')
file_url = [] # 存储fileURL的列表,每一个元素包括fileURL和对应日期
for i in text:
j = text[i]
f_url = j.get('fileURL') # 神奇的地方,这里一开始用正常键值对访问,只有fileURL会显示KeyError
f_date = j.get('start').split(' ')[0]
file_url.append((f_url, f_date))
# 浏览器页面最多只能显示两千条数据,但是url1里会包含所有数据的url2
print(file_url)
file_length = len(file_url)
print('Length of hdf files:' + str(file_length) + '\n')
break
except:
print('尝试爬取file URL失败,可能原因如下:1.url已更新;(2.cookies有误或失效;)3.网页json已更新')
# 保持下载状态,这里指下载全部文件的一个周期
while True:
count = 1
downloaded = 0
skip = 0
root = 'upload/'
# 解决新的防卡死问题,考虑headers问题
for i in file_url:
try:
path = root + i[1] + '/' + kind
if not os.path.exists(path): # 如果文件目录不存在
os.makedirs(path) # os.mkdir()只能创建一级目录,而os.makedirs()可以创建多级目录
url2 = url2_head + i[0]
filepath = path + url2.split("/")[-1]
if os.path.exists(filepath):
print(str(count) + '.文件已存在\n')
count += 1
skip += 1
continue
re = nasa.getPage(url2, cookie)
hdf = re.content
print(str(count) + '.已获取下载目标,准备下载')
with open(filepath, 'wb') as f:
f.write(hdf)
print('下载完成!\n')
f.close()
downloaded += 1
except:
print("下载第" + str(count) + "个文件失败,原因可能是连接出错\n")
count += 1
time.sleep(random.uniform(3, 4.5))
if downloaded + skip >= file_length:
break
# 遍历完一遍下载页面URL,长时间休眠一次
time.sleep(random.uniform(480, 720))
# 获取访问下载URL时Request Headers中的Cookie
def getCookie(driver_path, date_range, area_of_interest, username, password):
auto = autoClick.GetCookie(driver_path, date_range, area_of_interest, username, password)
cookie = auto.autoClick()
print('已获取cookie')
# 整合得到Request Headers中的Cookie
request_cookie = dict()
for i in cookie:
request_cookie[i['name']] = i['value']
assert (request_cookie != {})
print(request_cookie)
return request_cookie
if __name__ == '__main__':
# nasa = Nasa()
# date = nasa.getDate()
# area1 = '97,30,107,21'
# path = "chromedriver.exe"
# user = '......' # 个人账户及密码信息!
# code = '......'
# kind = {'upload/Surface temperature/': ['MOD11A1', 6],
# 'upload/Terra surface reflectance/': ['MOD09A1', 61]}
# area2 = 'x97y30,x107y21' # 文件url信息所在的url中,area信息格式与产品交互页面不同
# cookie = ''
#
# # 暂定每天爬取1次Cookie,3次遥感数据
# while True:
# if (datetime.datetime.now().hour == 0) and (datetime.datetime.now().minute == 00):
# cookie = getCookie(path, date, area1, user, code)
# time.sleep(3)
# nasa.getHdf(kind, date, area2, cookie)
# time.sleep(300)
# # 考虑计时任务
#
# if datetime.datetime.now().hour == 4 or datetime.datetime.now().hour == 8:
# if datetime.datetime.now().minute == 00:
# nasa.getHdf(kind, date, area2, cookie)
# time.sleep(600)
nasa = Nasa()
date = nasa.getDate()
area = '97,30,107,21'
path = "chromedriver.exe"
user = '......' # 个人账户及密码信息!
code = '......'
cookie = getCookie(path, date, area, user, code)
time.sleep(3)
kind = {'Surface temperature/': ['MOD11A1', 6], 'Terra surface reflectance/': ['MOD09A1', 61]} # 后续维护问题,观察网站变化
area = 'x97y30,x107y21' # 文件url信息所在的url中,area信息格式与产品交互页面不同
nasa.getHdf(kind, date, area, cookie)
'''
知识笔记:
在爬虫中,有时候处理需要登录才能访问的页面时,我们一般会直接将登录成功后获得的Cookies
放在请求头里面直接请求,一般不必重新登录(如果Cookies稳定不变)。
当浏览器下一次请求该网站时,浏览器会把此Cookies放到请求头一起提交给服务器,
Cookies携带了会话ID信息,服务器检查它即可找到对应的会话,再对其判断以辨认用户状态
如果传给服务器的Cookies无效,或者会话过期,则不能继续访问页面。
因此,Cookies需与对话配合,一个处于客户端,一个处于服务端。
每次通过HTTP协议访问一个资源的时候,浏览器都会自动在请求的header中加入你在这个域名下的所有cookies。
服务器在收到请求后可以根据是否有cookies以及cookies的值来判断你的身份,从而返回不同的资源。
(在登陆页面登陆后,服务器会会给你分配一个cookie,并放入返回的response的header中。
你的浏览器会储存这个cookie并在每次访问这个网站时都加入request的header中。
同时,服务器也会对给你分配的cookie的值进行配对存储,这样就能通过你的cookie过得你的身份了。)
当遇到403时,headers的内容越全越有希望,除了在get()方法中添加cookies参数,也可以在headers字典中添加cookies。
- 模拟登录:
(requests.Session()可以维持一个会话,并且自动处理Cookies)
1.利用网页开发工具,勾选preserve log;
2.清空Cookies,初次登陆,一般是查看Network里的第一个请求,观察Request Headers
和 Form Data里的独特信息,他们就是模拟登录需要的信息;
同时,也可以看到登陆后Response Headers中会有一个Set-Cookie,一般会与其他Cookie有关;
3.退出登录,清空Cookies,回到登录页面,
在Elements里找token。其他信息一般也可以通过这两项获得;
4.终极:selenium模拟浏览器登录后(能否先seesion登录),先尝试获取Cookies。
- 模拟点击:
需用代码精确模拟使用浏览器时的一系列操作。
'''
autoClick.py:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
#from selenium.webdriver.common.keys import Keys
import time
import random
__author__ = 'Ray'
class GetCookie:
def __init__(self, driver_path, date_range, area_of_interest, username, password):
self.driver_path = driver_path
self.date_range = date_range
self.area_of_interest = area_of_interest
self.username = username
self.password = password
def autoClick(self):
count = 1
while True:
# 1.创建Chrome浏览器对象,这会在电脑上在打开一个浏览器窗口
# 开启开发者工具(F12)
# option = webdriver.ChromeOptions()
# option.add_argument("--auto-open-devtools-for-tabs")
# browser = webdriver.Chrome(chrome_options=option,
# executable_path=self.driver_path)
# 为给webdriver的options增加参数excludeSwitches
option = webdriver.ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
browser = webdriver.Chrome(executable_path=self.driver_path, options=option)
#browser = webdriver.Chrome(executable_path=self.driver_path)
# browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
# "source": """
# Object.defineProperty(navigator, 'webdriver', {
# get: () => undefined
# })
# """
# })
try:
# 2.通过浏览器向服务器发送URL请求
url1 = 'https://ladsweb.modaps.eosdis.nasa.gov/search/order/4/MOD11A1--6/{}/DB/{}'.format(
self.date_range, self.area_of_interest) # 后续维护问题,观察网站变化,不过该模块目的只是获取cookie
# browser.get(
# 'https://ladsweb.modaps.eosdis.nasa.gov/search/order/4/MOD11A1--6/2021-02-22..2021-03-08/DB/97,30.1,107.1,21.2')
browser.get(url1)
time.sleep(1)
# 3.刷新浏览器
# browser.refresh()
# 4.设置浏览器的大小
browser.set_window_size(1400, 800)
# 5.登陆页面之前
profile = browser.find_element_by_id("profile-menu-option")
ActionChains(browser).move_to_element(profile).perform()
profile.click()
time.sleep(4.5) # !注意对手动使用浏览器时的精准模拟,这里会有近1秒的Loading,才会显示login按钮,前提是网络条件良好
login = browser.find_element_by_xpath("/html/body/nav/div/div/ul/li[6]/ul/li/a") # 后续维护问题,观察网站变化,暂时未找到更好的定位方式
login.click()
# 6.模拟登录
# 输入账号
input_account = browser.find_element_by_id('username')
input_account.send_keys(self.username)
# 输入密码
input_password = browser.find_element_by_id('password')
input_password.send_keys(self.password)
# 取消勾选保持登陆状态的选项
cancel_stay_in = browser.find_element_by_id('stay_in')
cancel_stay_in.click()
# 点击登录按钮
time.sleep(2)
login_button = browser.find_element_by_name('commit')
# builder = ActionChains(browser)
# builder.key_down(Keys.F12).perform()
login_button.click()
# print('=====================================')
# print("middle page url: " + str(browser.current_url))
# print("middle page title: " + str(browser.title))
# print("cookie信息为:" + str(browser.get_cookies())) # 多个Cookie
# print(browser.get_cookies)
# 7.回到产品页面,发现该页面下Cookie 各name-value组成的对能满足我们所需的request headers中的Cookie
time.sleep(5)
print('\n=====================================')
print("current url: " + str(browser.current_url))
print("current title: " + str(browser.title))
cookie = browser.get_cookies()
print('\n')
print(type(cookie))
print("cookie信息为:" + str(cookie)) # 多个Cookie
time.sleep(4)
# browser.quit() # 关闭所有窗口
return cookie
except:
browser.quit()
print('第' + str(count) + '次模拟点击过程中遇到问题\n')
time.sleep(random.uniform(5, 6))
count += 1
# 用于测试的主程序,实际投入时应运行nasa_modis.py中的主程序
if __name__ == '__main__':
path = "chromedriver.exe"
ranges = '2021-02-22..2021-03-08'
interest = '97,30.1,107.1,21.2'
user = '......' # 个人账户信息及密码!
code = '......'
auto_click = GetCookie(path, ranges, interest, user, code) # 可以def __init__(self, parameters):
cookie = auto_click.autoClick()
login.py(未使用selenium的模拟登录,项目未用上,仅供参考):
import requests
from requests import utils
from lxml import etree
import json
import time
'''
1.每一次访问登录页面,token都会更新
2.如果先在earthdata正页登录,进入产品页面还需走确认登录流程
'''
class Login:
def __init__(self):
self.headers1 = {'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3"}
self.headers2 = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36",
'Referer': 'https://ladsweb.modaps.eosdis.nasa.gov/search/order/4/MOD11A1--6/2021-02-22..2021-03-08/DB/97,30.1,107.1,21.2',
'Host': 'ladsweb.modaps.eosdis.nasa.gov',
'Accept': '*/*',
'Connection': 'keep-alive',
'X-Requested-With': 'XMLHttpRequest'}
self.login_url = 'https://urs.earthdata.nasa.gov'
self.post_url = 'https://urs.earthdata.nasa.gov/login'
# 测试用的logined_url
self.logined_url = 'https://ladsweb.modaps.eosdis.nasa.gov/api/v1/files/product=MOD11A1&collection=6&dateRanges=2021-02-22..2021-03-08&areaOfInterest=x97y30,x107y21&dayCoverage=true&dnboundCoverage=true'
self.session = requests.Session() # 神奇,会话处理Cookie
self.username = '......' # 个人账户信息及密码
self.password = '......'
def token(self):
response = self.session.get(self.login_url, headers=self.headers1, timeout=30) # 可能会被封ip
print('准备获取token')
selector = etree.HTML(response.text)
token = selector.xpath('//*[@id="login"]/input[2]/@value')[0] # 日了,获取属性不用text()
print('token: ' + str(token) + '\n')
return token
def getCookie(self):
pass
def login(self, cookie={}):
post_data = {
'commit': 'Log in',
'utf8': '✓',
'authenticity_token': self.token(),
'username': self.username,
'password': self.password,
} # 'redirect_uri': self.logined_url
response = self.session.post(self.post_url, data=post_data, headers=self.headers1) # 模拟登录
print('模拟登录成功!\n')
cookie = response.cookies # 里面有首次登录得到的Set-Cookie
cookie = utils.dict_from_cookiejar(cookie)
print(cookie)
print(type(cookie))
print('\n')
time.sleep(1)
response = self.session.get(self.logined_url, headers=self.headers2, timeout=30, cookies=cookie)
time.sleep(1)
# if response.status_code == 200:
# # 这里response headers里没看到cookie
# self.getHdf(response, cookie)
if __name__ == '__main__':
log = Login()
log.login()
标签:Modis,date,python,self,爬取,url,cookie,print,browser 来源: https://blog.csdn.net/u013598957/article/details/118493249