饿了么爬虫

饿了么爬虫

一次饿了么商品信息爬虫的记录

问题描述

  1. 网站:https://h5.ele.me/
  2. 要求:用户只需输入手机号,即可自动登录并爬取相应商家信息
  3. 描述:饿了么的商家信息全部需要登录后才能获取,登录过程需要输入手机号——>获取验证码——>输入验证码——>拖动滑块——>点击登录
  4. 完整项目结果:github项目

第一次尝试:手动获取Cookie+Html解析

1.1 实现步骤

  1. 从浏览器cookie里找到属于ele.me底下的SID,即为需要的cookie
  2. 将需要的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 运行结果

返回的html

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 步骤

  1. 老版本的登录界面查看网页元素,发现有在输入手机号后,发送了一个叫做mobile_send_code的请求
    手机号API
  2. 分析其输入验证码以后的response,并模拟发送相应请求
  3. 原博主是针对以前页面的做法,只在频繁访问后需要输入图形验证码,但是我在测试过程中发现需要滑动滑块进行验证,这一步无法模拟,所以此方法被迫舍弃

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 步骤

  1. 进入登录界面,查看各输入框及按钮的样式,采用find_element_by_xpath的方式操控某一元素
    如图所示,为滑块按钮的选中
    滑块元素
  2. 这个过程看起来容易,但在实际操作过程中,一直提示unable to locate element,我很奇怪,因为在审查元素时,确实可以找到。于是我使用page_source方法,查看源码,发现仍是未经渲染的,通过再度审查代码,才发现登录界面进行了多层iframe的嵌套,将登录的iframe嵌套在主页面下,又将滑块嵌套在登录的iframe下
    iframe嵌套
  3. 最后终于通过selenium完整模拟出了登录过程,并通过get_cookies()获取到所有cookie,再进一步处理后,塞给请求头
  4. 寻找页面数据来源,发现所有请求数据都是通过一个叫batch_shop的来获取的(后续测试发现,这个名字根据店铺分类有所不同)
    json数据
  5. 所以我们针对该数据来源,获取到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反反爬
动态数据来源查找
模拟滑块拖动