避踩雷 | 爬取美团门店信息中的“量子幽灵”

举报
Srius 发表于 2020/04/25 18:44:36 2020/04/25
1.2w+ 0 0
【摘要】 美团爬虫其实很早就有写过,除了薛定谔的验证码,其他的都还蛮好解决滴~为什么会单独开一个帖子呢,主要是因为做【不秃头 | 如何用ModelArts满足一个奶茶重度爱好者的好奇心】的时候需要用到美团里奶茶门店的数据,其中获取的时候遇到了一些问题,虽然最后解决了,但还是碰了许多壁,便想着记录下来,免得其他小伙伴大伙伴们又被这个奇怪的问题卡住辽~

美团爬虫其实很早就有写过,除了薛定谔的验证码,其他的都还蛮好解决滴~为什么会单独开一个帖子呢,主要是因为做【不秃头 | 如何用ModelArts满足一个奶茶重度爱好者的好奇心】的时候需要用到美团里奶茶门店的数据,其中获取的时候遇到了一些问题,虽然最后解决了,但还是碰了许多壁,便想着记录下来,免得其他小伙伴大伙伴们又被这个奇怪的问题卡住辽~


目标:爬取北京市所有coco门店的奶茶图片和标签

也就是这一部分啦~

image.png

工具:python3.7 + selenium + Chrome79

在写爬虫的前几天(大概是4月中旬)Chrome自己悄悄更新辽~而selenium还不支持最新的Chrome,于是需要先整一个老版本的Chrome浏览器~(当然用其他家的浏览器也是阔以滴~)

为啥用selenium呢,因为不用配置各种奇奇怪怪的头部设置辽,一部分用JS生成的html代码也能够直接读到,而且当薛定谔的美团验证码出现时还可以直接手动输入~

(使用selenium前需要配置对应浏览器的webdriver,百度上太多辽就不写了~)

导入库的代码如下: 

from selenium import webdriver
import requests
import time

step1:定位到北京市coco门店列表

打开美团官网,定位到搜索栏,输入搜索关键词“coco”,再点击右侧的搜索按钮,就能够进入门店列表~

image.png

代码如下:

driver = webdriver.Chrome()
url = 'https://www.meituan.com/'
 
driver.get(url)
driver.find_element_by_xpath('//*[@id="main"]/header/div[2]/div[2]/div[1]/input').send_keys('coco')
driver.find_element_by_xpath('//*[@id="main"]/header/div[2]/div[2]/div[1]/button/span').click()

有小伙伴可能会问,这个find_element_by_xpath意思是根据xpath定位元素我知道,后面这堆xpath路径你是怎么找的,总不能人工数吧?

当然不是,鼠标放到想要获取路径的元素上,单击右键——检查,在右侧找到目标元素,再单击右键——Copy——Copy XPath就阔以辽~(我使用的是Chrome,其他浏览器可能会有一些区别)

image.png

最后就会进入这样的界面

image.png

step2:采集每个门店的链接和“下一页”按钮的链接

检查源代码,发现整个门店列表都被很整齐滴封装在一个div里

image.png

打开其中一个div,链接就在里面

image.png

于是很方便地就能定位到每个链接并将他们保存起来,代码如下所示:

list = driver.find_element_by_class_name('common-list-main').find_elements_by_class_name('default-card')
    links = []
    for l in list:
        link = l.find_element_by_tag_name('a').get_attribute('href')
        links.append(link)

接着就是找下一页的链接了

通过元素进行定位,发现它有一个很长的class属性right-arrow iconfont icon-btn_right,嗯,应该可以用它定位了。

image.png

测试运行,程序却提示没有定位到这个元素,于是我换了一个思路,首先定位到了这个页码列表,读取里面所有的a标签。

image.png

再取这个列表的最后一位,便成功实现了翻页。

代码如下:

driver.find_element_by_class_name('mt-pagination').find_elements_by_tag_name('a')[-1].click()

step3:采集每个门店主页中的奶茶图片和标签

进入页面之后,Chrome显示的是这个亚子~(请记住这一点,后面非常关键~)

image.png

定位到想要爬的元素,嗯,在一个列表里,挺好定位获取的应该~

结果没想到这里折腾了我好久…

首先想到的定位这个列表,再获取所有的li标签,在每个li标签里分别找到图片链接和图片标签,保存起来~结果发现定位不了。

那换个思路,反正每个门店里都只有6个图,那就直接for循环个6次,用xpath来定位~结果还是找不到这些元素。

难道是我元素定位方式有问题?试了试爬其他部分的信息,完全OK。

这下我彻底迷惑了~

正在纠结之时,忽然发现一个细节,在通过“检查”定位图片源代码时,第一次没有像其他元素那样直接定位在对应的源代码上,而是停在了那一大块的位置。再次点击“检查”,这才定位到目标位置。

难道?这个玩意是“量子态”的,必须要看到了才会出现,不看就没有吗?

于是我加上了一段滑动滚轮的代码,让网页从打开时的样子(即上面提到的那个亚子)变成下面这个样子~

image.png

然后再次运行爬虫,我惊奇地发现,它正常了!(真·量子态的图片栏)

附上这部分的代码~

js = 'window.open("%s");' % link
driver.execute_script(js)
windows = driver.window_handles
driver.switch_to.window(windows[-1])
js = 'window.scrollTo(0,1000);'
driver.execute_script(js)
ul = driver.find_element_by_xpath('//*[@id="app"]/section/div/div[3]/div[1]/div[2]/div/ul')
lis = ul.find_elements_by_tag_name('li')
time.sleep(2)

step4:下载图片

最后一步便是根据爬取到的图片链接下载图片。

使用requests库进行图片的下载(基本操作,无需多说~)

代码如下:

pic_url = div.find_element_by_tag_name('img').get_attribute('src')
tag = div.find_element_by_tag_name('span').text
data = requests.get(pic_url).content
with open('C:/Users/Sirius/Desktop/data/%s_%d.jpg' % (tag, n), 'wb') as f:
    f.write(data)
print('已保存%s-%d' % (tag, n))
n += 1

一些细节

  1. 由于打开门店页面时会打开一个新的浏览器窗口,需要切换一下selenium的当前窗口,代码如下:

windows = driver.window_handles
driver.switch_to.window(windows[-1])

同样,在爬取当前门店结束后需要关闭这个窗口,并切换回原来的那一个。

driver.close()
windows = driver.window_handles
driver.switch_to.window(windows[0])

2.保存文件时不能直接用标签,不然会把之前的图片给覆盖掉~

源代码

最后附上完整版的代码~

from selenium import webdriver
import requests
import time
 
driver = webdriver.Chrome()
url = 'https://www.meituan.com/'
 
driver.get(url)
driver.find_element_by_xpath('//*[@id="main"]/header/div[2]/div[2]/div[1]/input').send_keys('coco')
driver.find_element_by_xpath('//*[@id="main"]/header/div[2]/div[2]/div[1]/button/span').click()
 
n = 1
x = 0
 
while(x < 100):
    list = driver.find_element_by_class_name('common-list-main').find_elements_by_class_name('default-card')
    links = []
    for l in list:
        link = l.find_element_by_tag_name('a').get_attribute('href')
        links.append(link)
    for link in links:
        try:
            js = 'window.open("%s");' % link
            driver.execute_script(js)
            windows = driver.window_handles
            driver.switch_to.window(windows[-1])
            js = 'window.scrollTo(0,1000);'
            driver.execute_script(js)
            ul = driver.find_element_by_xpath('//*[@id="app"]/section/div/div[3]/div[1]/div[2]/div/ul')
            lis = ul.find_elements_by_tag_name('li')
            time.sleep(2)
            for div in lis:
                pic_url = div.find_element_by_tag_name('img').get_attribute('src')
                tag = div.find_element_by_tag_name('span').text
                data = requests.get(pic_url).content
                with open('C:/Users/Sirius/Desktop/data/%s_%d.jpg' % (tag, n), 'wb') as f:
                    f.write(data)
                print('已保存%s-%d' % (tag, n))
                n += 1
        except Exception as e:
            print(e)
        driver.close()
        windows = driver.window_handles
        driver.switch_to.window(windows[0])
    driver.find_element_by_class_name('mt-pagination').find_elements_by_tag_name('a')[-1].click()
    time.sleep(3)
    x += 1
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。