【问题标题】:Selenium scrolling and scraping with BeautifulSoup produces duplicate results使用 BeautifulSoup 进行 Selenium 滚动和抓取会产生重复的结果
【发布时间】:2018-11-08 18:29:14
【问题描述】:

我有这个脚本可以从 Instagram 下载图片。我遇到的唯一问题是,当 Selenium 开始向下滚动到网页底部时,BeautifulSoup 在循环请求后开始抓取相同的 img src 链接。

虽然它会继续向下滚动并下载图片,但在完成之后,我最终会有 2 或 3 个重复。所以我的问题是有没有办法防止这种重复发生?

import requests
from bs4 import BeautifulSoup
import selenium.webdriver as webdriver


url = ('https://www.instagram.com/kitties')
driver = webdriver.Firefox()
driver.get(url)

scroll_delay = 0.5
last_height = driver.execute_script("return document.body.scrollHeight")
counter = 0

print('[+] Downloading:\n')

def screens(get_name):
    with open("/home/cha0zz/Desktop/photos/img_{}.jpg".format(get_name), 'wb') as f:
        r = requests.get(img_url)
        f.write(r.content)

while True:

    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(scroll_delay)
    new_height = driver.execute_script("return document.body.scrollHeight")

    soup = BeautifulSoup(driver.page_source, 'lxml')
    imgs = soup.find_all('img', class_='_2di5p')
    for img in imgs:
        img_url = img["src"]
        print('=> [+] img_{}'.format(counter))
        screens(counter)
        counter = counter + 1

    if new_height == last_height:
        break
    last_height = new_height


更新: 所以我把这部分代码放在while True之外,让selenium首先加载整个页面,希望bs4能刮掉所有的图像。它只工作到 30 号,然后停止。

soup = BeautifulSoup(driver.page_source, 'lxml')
imgs = soup.find_all('img', class_='_2di5p')
for img in imgs:
    #tn = datetime.now().strftime('%H:%M:%S')
    img_url = img["src"]
    print('=> [+] img_{}'.format(counter))
    screens(counter)
    counter = counter + 1

【问题讨论】:

    标签: python selenium beautifulsoup


    【解决方案1】:

    我会使用 os 库来检查文件是否已经存在

    import os
    
    
    def screens(get_name):
        with open("/home/cha0zz/Desktop/photos/img_{}.jpg".format(get_name), 'wb') as f:
            if os.path.isfile(path/to/the/file):      #checks file exists. Gives false on directory
        # or if os.path.exists(path/to/the/file): #checks file/directory exists
                pass
            else:
                r = requests.get(img_url)
                f.write(r.content)
    

    *我可能弄乱了 if 和 with 语句的顺序

    【讨论】:

    • 问题是它在 selenium 加载页面时获取相同的 url。
    • 嗯....所以?你说你有文件重复。我的解决方案是在保存文件之前检查文件是否存在以避免重复。如果我误解了你,请纠正我。
    • 另外,关于您的更新。我认为它最多加载 30 个,因为 DOM 加载得那么远。您可能想尝试一直滚动到页面底部,然后才 soup = BeautifulSoup(driver.page_source, 'lxml')。 @uzdisral
    • selenium 会加载整个页面,但汤不会全部下载。它达到 30 最大值并停止
    【解决方案2】:

    它在您的脚本的第二个版本中仅加载 30 个的原因是因为其余元素已从页面 DOM 中删除,并且不再是 BeautifulSoup 看到的源的一部分。解决方案是继续做你第一次做的事情,但在遍历列表并调用screens()之前删除所有重复的元素。你可以这样做using sets,如下所示,但我不确定这是否是绝对最有效的方法:

    import requests
    import selenium.webdriver as webdriver
    import time
    
    driver = webdriver.Firefox()
    
    url = ('https://www.instagram.com/cats/?hl=en')
    driver.get(url)
    
    scroll_delay = 3
    last_height = driver.execute_script("return document.body.scrollHeight")
    counter = 0
    
    print('[+] Downloading:\n')
    
    def screens(get_name):
        with open("test_images/img_{}.jpg".format(get_name), 'wb') as f:
            r = requests.get(img_url)
            f.write(r.content)
    
    old_imgs = set()
    
    while True:
    
        imgs = driver.find_elements_by_class_name('_2di5p')
    
        imgs_dedupe = set(imgs) - set(old_imgs)
    
        for img in imgs_dedupe:
            img_url = img.get_attribute("src")
            print('=> [+] img_{}'.format(counter))
            screens(counter)
            counter = counter + 1
    
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(scroll_delay)
        new_height = driver.execute_script("return document.body.scrollHeight")
    
        old_imgs = imgs
    
        if new_height == last_height:
            break
        last_height = new_height
    
    driver.quit()
    

    如您所见,我使用不同的页面进行测试,其中包含 420 张猫的图片。结果是 420 张图片,即该帐户上的帖子数,其中没有重复。

    【讨论】:

    • 这绝对是完美的!正是我需要做的。为此非常感谢。你给我看有什么好的读物或一些样本吗?你说使用sets()
    • 如果您想要关于集合的信息,您可以查看集合的Python documentation,尤其是difference() 方法,该方法允许您通过执行 A 删除 A 中也出现在 B 中的重复项- B. 我通过查看大量资源得出了这个答案,但我没有找到任何值得推荐的好东西。
    • 好的,没问题。我也看到你从soup换成了imgs = driver.find_elements_by_class_name('_2di5p')所以基本上soup就不用用了吧?
    • 是的,我去掉了BeautifulSoup,因为它没有必要,因为 Selenium 也可以解析元素。如果你可以避免它,那么有额外的依赖是没有意义的。我想知道你是否也可以不使用requests,但它似乎工作得很好,所以我认为没有理由改变它。
    • 它似乎可以正常工作。我只是稍微清理一下代码,想办法静默运行webdriver,所以它不会每次都打开浏览器
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 2019-05-06
    • 2020-02-26
    • 1970-01-01
    • 2022-11-07
    • 2021-09-30
    相关资源
    最近更新 更多