我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!
页面分析
- 登录、设置地址、查找目标店铺
这些步骤都人工操作了,就不需要分析页面了。唯一碰到的问题是,使用PC浏览器登录时,发送验证码的滚动条验证步骤无法通过,需要使用手机浏览器成功发送验证码后,将验证码填入PC端登录。
- 巨坑注意:虽然PC端无法通过滚动条验证步骤,但是该动作还是要做的,否则即使填入了手机获取到的验证码,是无法提交登录的。(与甲方共同测试时,在这个环节卡了好久)
- 目标店铺数据分析
该步骤不难,F12打开chrome的开发者模式,进入店铺后,使用关键词搜索一下,就能发现,所有的菜品,都在一个food的response中
编辑
手机页面是竖屏,把窗口收窄能够有更好的浏览体验
编辑
人工复制数据的步骤
- 图片目标分析
图片的请求则相当简单,只需要设置请求头,再使用GET请求图片网址即可,而图片网址在目标店铺的数据中,每个菜品都有对应的图片网址。
编辑
图片网址请求
解决步骤与代码
- 将人工获取的店铺数据存入TXT文件,放在一个目录下,如下图:
编辑
人工获取的数据
- 有了数据之后,遍历、读取、加工数据、下载图片就都是基础的json、pandas与requests的操作了,具体详见代码注释吧。
`
页面分析
- 登录、设置地址、查找目标店铺
这些步骤都人工操作了,就不需要分析页面了。唯一碰到的问题是,使用PC浏览器登录时,发送验证码的滚动条验证步骤无法通过,需要使用手机浏览器成功发送验证码后,将验证码填入PC端登录。
- 巨坑注意:虽然PC端无法通过滚动条验证步骤,但是该动作还是要做的,否则即使填入了手机获取到的验证码,是无法提交登录的。(与甲方共同测试时,在这个环节卡了好久)
- 目标店铺数据分析
该步骤不难,F12打开chrome的开发者模式,进入店铺后,使用关键词搜索一下,就能发现,所有的菜品,都在一个food的response中
编辑
手机页面是竖屏,把窗口收窄能够有更好的浏览体验
编辑
人工复制数据的步骤
- 图片目标分析
图片的请求则相当简单,只需要设置请求头,再使用GET请求图片网址即可,而图片网址在目标店铺的数据中,每个菜品都有对应的图片网址。
编辑
图片网址请求
解决步骤与代码
- 将人工获取的店铺数据存入TXT文件,放在一个目录下,如下图:
编辑
人工获取的数据
- 有了数据之后,遍历、读取、加工数据、下载图片就都是基础的json、pandas与requests的操作了,具体详见代码注释吧。
页面分析
- 登录、设置地址、查找目标店铺
这些步骤都人工操作了,就不需要分析页面了。唯一碰到的问题是,使用PC浏览器登录时,发送验证码的滚动条验证步骤无法通过,需要使用手机浏览器成功发送验证码后,将验证码填入PC端登录。
- 巨坑注意:虽然PC端无法通过滚动条验证步骤,但是该动作还是要做的,否则即使填入了手机获取到的验证码,是无法提交登录的。(与甲方共同测试时,在这个环节卡了好久)
- 目标店铺数据分析
该步骤不难,F12打开chrome的开发者模式,进入店铺后,使用关键词搜索一下,就能发现,所有的菜品,都在一个food的response中
编辑
手机页面是竖屏,把窗口收窄能够有更好的浏览体验
编辑
人工复制数据的步骤
- 图片目标分析
图片的请求则相当简单,只需要设置请求头,再使用GET请求图片网址即可,而图片网址在目标店铺的数据中,每个菜品都有对应的图片网址。
编辑
图片网址请求
解决步骤与代码
- 将人工获取的店铺数据存入TXT文件,放在一个目录下,如下图:
编辑
人工获取的数据
- 有了数据之后,遍历、读取、加工数据、下载图片就都是基础的json、pandas与requests的操作了,具体详见代码注释吧。
@classmethod def parse_data(cls, filename: pl.Path) -> tuple: """ 解析获取到的美团店铺数据 :param filename: 存储数据的文件路径 :return: """ with open(filename, 'r', encoding='utf-8') as fin: data = fin.read() data = json.loads(data) # 解析数据步骤 shop_name = data['data']['poi_info']['name'] data = data['data']['food_spu_tags'] df = pd.DataFrame() for tag in data: dfx = pd.DataFrame(tag['spus']) dfx['分类'] = tag['name'] df = pd.concat([df, dfx]) df = df.loc[df['分类'].map(lambda x: x not in ['折扣', '热销', '推荐'])] df['原价'] = df.apply(cls.get_origin_price, axis=1) df.reset_index(inplace=True, drop=True) return shop_name, df
@classmethod def download_picture(cls, url: str, filename: pl.Path): """ 下载图片的方法 :param url: 图片的地址 :param filename: 输出图片的路径(含文件名) :return: """ # 初始化请求头 headers = { "accept": "image/avif,image/webp,image/apng,image/svg+xml,image/,/*;q=0.8", "accept-encoding": "gzip, deflate, br", "accept-language": "zh-CN,zh;q=0.9", "referer": "h5.waimai.meituan.com/", "sec-ch-ua": "" Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": ""Windows"", "sec-fetch-dest": "image", "sec-fetch-mode": "no-cors", "sec-fetch-site": "cross-site", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36" } # 下载文件 file = requests.get(url, headers, stream=True) with open(filename, "wb") as code: for chunk in file.iter_content(chunk_size=1024): # 边下载边存硬盘 if chunk: code.write(chunk)
@classmethod def get_pictures(cls, shop_name: str, data: pd.DataFrame, pic_dir: pl.Path): """ 批量获取图片数据的方法 :param shop_name: 店铺名 :param data: 数据 :param pic_dir: 图片存放的目录 :return: """ print(f'开始下载店铺:{shop_name} 的图片') # 下载前按照菜品名称与图片地址进行去重处理,减少请求数量 download_data = data.copy() download_data = download_data.drop_duplicates(['name', 'picture'], keep='last') # 筛选去除图片地址为空的 download_data = download_data.loc[ (download_data['picture'].map(lambda x: pd.notnull(x))) | (download_data['picture'] != '') ] max_len = len(download_data) for idx, food in enumerate(download_data.to_dict(orient='records')): # 遍历数据 pic_url = food['picture'] # 拆分获取图片扩展名 suffix = pl.Path(pic_url.split('/')[-1]).suffix # 加工出图片的路径(包含名称) name = food['name'].replace('', '').replace('/', '') filename = pic_dir / f"{name}{suffix}" # 使用下载方法下载 try: cls.download_picture(pic_url, filename) print(f'({idx+1}/{max_len})菜品:{food["name"]} 图片下载完成') except Exception as e: print(f'!!!({idx+1}/{max_len})菜品:{food["name"]} 图片下载失败,错误提示是: {e}') # 随机暂停 time.sleep(randint(1, 3) / 10)
@classmethod def write_data(cls, shop_name, data, shop_dir): """ 将数据输出至excel文件 :param shop_name: 店铺名 :param data: 数据 :param shop_dir: 店铺存放的文件夹 :return: """ data.to_excel(shop_dir / f'{shop_name}.xlsx', index=False)
def run(self): """ 运行程序 :return: """ try: for filename in self.file_path.iterdir(): # 先解析人工取得的数据 shop_name, data = self.parse_data(filename) # 再创建文件夹 shop_dir, pic_dir = self.create_dir(shop_name) # 写入Excel文件 self.write_data(shop_name, data, shop_dir) # 获取图片 self.get_pictures(shop_name, data, pic_dir) print(f'店铺:{shop_name}的数据已解析下载完毕,数据存储在:“{shop_dir.absolute()}”路径下') return True, None except Exception as e: return False, e
if name == 'main': spider = SpiderObj() res, err = spider.run() if res: input('程序已运行完毕,按回车键退出') else: input(f'程序运行出错,错误提示是: {err}')

1. ## 程序运行

编辑
大功告成
## **总结**
不管全自动还是半自动,能解决问题的都是好爬虫。最后实现的爬虫实际上使用到的知识点都不是困难的:
1. 读取文件,使用json、pandas解析数据输出Excel表格;
1. 使用for循环,requests的get请求下载图片
1. 创建文件夹,去重、try-except等
至于回避的那三个难点留给某位大老板用Money激发我去攻克吧^_^
## 我是政胤 期待你的关注
大功告成
## **总结**
不管全自动还是半自动,能解决问题的都是好爬虫。最后实现的爬虫实际上使用到的知识点都不是困难的:
1. 读取文件,使用json、pandas解析数据输出Excel表格;
1. 使用for循环,requests的get请求下载图片
1. 创建文件夹,去重、try-except等
至于回避的那三个难点留给某位大老板用Money激发我去攻克吧^_^
## 我是政胤 期待你的关注
大功告成
## **总结**
不管全自动还是半自动,能解决问题的都是好爬虫。最后实现的爬虫实际上使用到的知识点都不是困难的:
1. 读取文件,使用json、pandas解析数据输出Excel表格;
1. 使用for循环,requests的get请求下载图片
1. 创建文件夹,去重、try-except等
至于回避的那三个难点留给某位大老板用Money激发我去攻克吧^_^
## 我是政胤 期待你的关注