饿了么爬虫
一次饿了么商品信息爬虫的记录
问题描述
- 网站:
https://h5.ele.me/
- 要求:用户只需输入手机号,即可自动登录并爬取相应商家信息
- 描述:饿了么的商家信息全部需要登录后才能获取,登录过程需要输入手机号——>获取验证码——>输入验证码——>拖动滑块——>点击登录
- 完整项目结果:github项目
第一次尝试:手动获取Cookie+Html解析
1.1 实现步骤
- 从浏览器cookie里找到属于ele.me底下的SID,即为需要的cookie
- 将需要的header和cookie利用requests包发送给网站(代码如下),但是发现返回的源码并不是从审查元素里见到的,很明显设置了反爬机制
1.2 代码
def get_page():
url = 'https://h5.ele.me/shop/#geohash=wsbrg7bzzpgt&id=E6139125827790262630&rank_id=undefined&spm=a2ogi.13147251.shopList.5'
cookie = {
'SID': 'KQAAAAEesy9K7AAGDwBbLyKEmp8S7L5nCS8HfVsyodeBoyI9Ym_U0kFd',
}
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/71.0.3578.98 Safari/537.36',
'Referer': 'https://h5.ele.me/shop/#geohash=wsbrg7bzzpgt&id=E6139125827790262630&rank_id=undefined&spm=a2ogi.13147251.shopList.5',
}
# re = json.loads(requests.get(url,headers=header,cookies=cookie).text)
re = requests.get(url,headers=header,cookies=cookie).text
return re
re = get_page()
print(re)
1.3 运行结果
1.4 附加处理
因为之前以为可以直接对html进行处理,所以手动保存了html后,先写了一个针对html进行数据爬取的脚本
完整代码可查看github项目链接
#对html进行解析
#([\s\S]*使用正则提取所有的字符串,?表示使用使用非贪婪模式
goodslist=re.findall(r'<span class="fooddetails-nameText_250s_">([\s\S]*?)</span>',html)
#-?\d+\.?\d*e?-?\d*?提取所有的数字,包括小数
# currentpricelist=re.findall(r'¥</span>(-?\d+\.?\d*e?-?\d*?)</div>',html)
text=re.sub('\s+'," ",html)
description=re.findall(r'<p class="fooddetails-desc_3tvBJ">([\s\S]*?)</p>',text)
# description=re.sub('\s+'," ",description)
print(description)
#将list转为datafarme
goods=pd.DataFrame(goodslist)
descriptions=pd.DataFrame(description)
#横向拼接
result=pd.concat([goods,descriptions],axis=1)
#更改dataframe列名称
result.columns=['商品名','描述信息']
#保存文件
result.to_excel(r"./result.xlsx",index=None)
第二次尝试:直接利用登录相关API
2.1 步骤
- 在老版本的登录界面查看网页元素,发现有在输入手机号后,发送了一个叫做mobile_send_code的请求
- 分析其输入验证码以后的response,并模拟发送相应请求
- 原博主是针对以前页面的做法,只在频繁访问后需要输入图形验证码,但是我在测试过程中发现需要滑动滑块进行验证,这一步无法模拟,所以此方法被迫舍弃
2.2 代码
import requests
import json
headers = {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json; charset=UTF-8',
'Origin': 'https://h5.ele.me',
'Referer': 'https://h5.ele.me/login/',
'Sec-Fetch-Mode': 'cors',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'
}
phoneNum = input('请输入你的手机号,回车键确认:')
# 模拟点击发送验证码请求
send_msg_url = 'https://h5.ele.me/restapi/eus/login/mobile_send_code'
send_msg_payloadData = {"mobile":phoneNum, "captcha_value":"", "captcha_hash":"", "scf":"ms"}
res1 = requests.post(url=send_msg_url, data=json.dumps(send_msg_payloadData), headers=headers)
token = res1.json()['validate_token']
# 模拟登录请求
send_msgCode_url = 'https://h5.ele.me/restapi/eus/login/login_by_mobile'
my_code = input('请输入你的验证码,回车键确认:')
send_msgCode_payloadData = {"mobile":phoneNum, "validate_code":my_code, "validate_token":token, "scf":"ms"}
res2 = requests.post(url=send_msgCode_url, data=json.dumps(send_msgCode_payloadData), headers=headers)
print(res2.text)
第三次尝试:利用selenium模拟滑块滑动
3.1 步骤
- 进入登录界面,查看各输入框及按钮的样式,采用find_element_by_xpath的方式操控某一元素
如图所示,为滑块按钮的选中
- 这个过程看起来容易,但在实际操作过程中,一直提示unable to locate element,我很奇怪,因为在审查元素时,确实可以找到。于是我使用page_source方法,查看源码,发现仍是未经渲染的,通过再度审查代码,才发现登录界面进行了多层iframe的嵌套,将登录的iframe嵌套在主页面下,又将滑块嵌套在登录的iframe下
- 最后终于通过selenium完整模拟出了登录过程,并通过get_cookies()获取到所有cookie,再进一步处理后,塞给请求头
- 寻找页面数据来源,发现所有请求数据都是通过一个叫batch_shop的来获取的(后续测试发现,这个名字根据店铺分类有所不同)
- 所以我们针对该数据来源,获取到json格式的商品信息,并经过处理,写入表格
3.2 代码
完整代码可查看github项目
driver.get("https://tb.ele.me/wow/msite/act/login")
# 等到登录页面出现再操作
WebDriverWait(driver,10).until(EC.frame_to_be_available_and_switch_to_it("alibaba-login-box"))
# print(driver.page_source)
# 获取并自动填充手机号
phone = driver.find_element_by_xpath('//input[@id="fm-sms-login-id"]')
phonenumber = input('请输入手机号,回车键确认:')
phone.send_keys(phonenumber)
# 发送验证码按钮
driver.find_element_by_xpath("//a[@class='send-btn-link']").click()
# 以前的协议同意按钮
# driver.find_element_by_css_selector("input[@id='fm-agreement-checkbox']").click()
# 验证码发送前的滑块验证
WebDriverWait(driver,10).until(EC.frame_to_be_available_and_switch_to_it("baxia-dialog-content"))
fuck=driver.find_element_by_xpath('//span[@id="nc_2_n1z"]')
action =ActionChains(driver)
action.click_and_hold(fuck)
action.move_by_offset(300,0)
action.release().perform()
# 验证码输入及填充
# 此次需返回父级iframe,才能获取到验证码输入等元素
vali = input('请输入验证码,回车键确认:')
driver.switch_to.parent_frame()
driver.find_element_by_xpath('//input[@id="fm-smscode"]').send_keys(vali)
# 点击登录
driver.find_element_by_xpath("//button[@type='submit']").click()
3.3 运行结果
3.4 演示效果
总结
这是第一次彻底研究整个爬虫过程,有一说一,阿里系的东西,确实比较难搞,网上很多东西已经无法再使用,再加上要求的特殊性,cookie这一部分十分难自动获取,所以最后只能尝试使用selenium。一天多的时间也学到了很多新东西,虽然还有些并没有完全吃透,比如说js的反爬应该如何处理等,而且最后完成的项目也比较粗糙,在指定商户方面、登录失败的提示方面都没有写比较全面的处理机制,不过也仅以此作为一次学习的记录,欢迎指导交流~嘿嘿嘿!:)
参考博客
selenium多层iframe跳转
饿了么模拟登录请求
element定位方法
反爬机制总结
selenium反反爬
动态数据来源查找
模拟滑块拖动