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

举报
Srius 发表于 2020/04/25 18:44:36 2020/04/25
【摘要】 美团爬虫其实很早就有写过,除了薛定谔的验证码,其他的都还蛮好解决滴~为什么会单独开一个帖子呢,主要是因为做【不秃头 | 如何用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

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

全部回复

上滑加载中

设置昵称

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

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

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