You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Selenium爬取Meta广告库时出现'NoneType'无startswith属性错误

解决Meta广告库爬取中的元素识别错误与CTA链接提取问题

错误原因

'NoneType' object has no attribute 'startswith'错误的本质是:当前选中的元素不包含aria-controls属性,调用get_attribute('aria-controls')返回None,而None没有startswith方法,因此触发报错。

修复方案与优化代码

1. 安全处理属性判断

先获取aria-controls属性值,判断其不为空后再检查前缀,避免None调用方法:

# 替换原有的判断逻辑
aria_controls = current_element.get_attribute('aria-controls')
if aria_controls and aria_controls.startswith("js_"):
    print("New ad - Traversing...")
    new_ad += 1
else:
    print("still traversing...")

2. 替换不稳定的TAB遍历方式

用TAB切换元素的方式极易定位到非广告元素,建议直接通过广告卡片的特征批量定位:

# 替换原有的tab遍历循环部分
last_height = browser.execute_script("return document.body.scrollHeight")
while True:
    # 定位当前页面所有广告卡片
    ads = WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located(
        (By.XPATH, "//div[contains(@class, 'x1n2onr6') and @role='article']")
    ))
    
    for ad in ads:
        try:
            # 查找广告内的CTA按钮
            cta_buttons = ad.find_elements(By.XPATH, ".//div[@role='button' and @aria-busy='false' and @tabindex='0']")
            for button in cta_buttons:
                button_text = button.text.strip()
                if button_text in meta_cta_buttons:
                    # 向上遍历父节点直到找到a标签
                    parent_element = button
                    while parent_element.tag_name != 'a':
                        parent_element = parent_element.find_element(By.XPATH, "..")
                    cta_url = parent_element.get_attribute('href')
                    unique_store_urls.add(cta_url)
                    ads_traversed += 1
                    print(f"Ad traversed! Going next...")
        except NoSuchElementException:
            continue
    
    # 处理无限滚动
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(3)
    new_height = browser.execute_script("return document.body.scrollHeight")
    
    # 检查是否到达页脚或无新内容
    try:
        WebDriverWait(browser, 5).until(EC.presence_of_element_located(
            (By.XPATH, "//a[contains(text(),'Ad Library API')]")
        ))
        print("到达页面底部,停止爬取")
        break
    except TimeoutException:
        pass
    
    if new_height == last_height:
        print("无新内容加载,停止爬取")
        break
    last_height = new_height

3. 完整修复后的代码

import time
from datetime import datetime, timedelta
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException

# 爬取Meta广告库CTA链接的函数
def get_facebook_ads():

    past_date = (datetime.now() - timedelta(days=3)).strftime("%m/%d/%Y")
    meta_cta_buttons = ['Get Offer', 'Get offer', 'Open Link', 'Open link', 'Order Now', 'Order now', 'Save', 'Shop Now', 'Shop now', 'Subscribe', 'Learn More', 'Learn more', 'Contact Us', 'Contact us', 'Download']
    unique_store_urls = set()

    try:
        # 初始化浏览器并导航到目标页面
        browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
        browser.get("https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&q=%22%20%22&sort_data[direction]=desc&sort_data[mode]=relevancy_monthly_grouped&search_type=keyword_exact_phrase&media_type=all")
        # 处理搜索框
        search_box = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, "//input[@placeholder='Search by keyword or advertiser']")))
        search_box.click()
        search_box.clear()
        search_box.send_keys("" "" + Keys.ENTER)
        time.sleep(3)

        ads_traversed = 0
        last_height = browser.execute_script("return document.body.scrollHeight")
        
        while True:
            # 定位当前页面所有广告卡片
            ads = WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located(
                (By.XPATH, "//div[contains(@class, 'x1n2onr6') and @role='article']")
            ))
            
            for ad in ads:
                try:
                    # 查找广告内的CTA按钮
                    cta_buttons = ad.find_elements(By.XPATH, ".//div[@role='button' and @aria-busy='false' and @tabindex='0']")
                    for button in cta_buttons:
                        button_text = button.text.strip()
                        if button_text in meta_cta_buttons:
                            # 向上遍历父节点直到找到a标签
                            parent_element = button
                            while parent_element.tag_name != 'a':
                                parent_element = parent_element.find_element(By.XPATH, "..")
                            cta_url = parent_element.get_attribute('href')
                            unique_store_urls.add(cta_url)
                            ads_traversed += 1
                            print(f"Ad traversed! Going next...")
                except NoSuchElementException:
                    continue
            
            # 处理无限滚动
            browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(3)
            new_height = browser.execute_script("return document.body.scrollHeight")
            
            # 检查是否到达页脚或无新内容
            try:
                WebDriverWait(browser, 5).until(EC.presence_of_element_located(
                    (By.XPATH, "//a[contains(text(),'Ad Library API')]")
                ))
                print("到达页面底部,停止爬取")
                break
            except TimeoutException:
                pass
            
            if new_height == last_height:
                print("无新内容加载,停止爬取")
                break
            last_height = new_height

        # 打印去重后的链接列表
        print("\n")
        for index, value in enumerate(unique_store_urls, start=1):
            print(f"{index}. {value}")            
    except Exception as e:
        print(e)
        browser.quit() 
        return unique_store_urls

def main():
    get_facebook_ads()
if __name__ == "__main__":
    main()

关键优化点

  • 增加属性非空判断,避免None调用方法报错
  • 替换TAB遍历为直接定位广告卡片,提升爬取稳定性
  • 优化无限滚动逻辑,通过滚动高度和页脚元素判断终止条件
  • 简化CTA按钮的查找逻辑,直接基于广告卡片范围搜索

内容的提问来源于stack exchange,提问作者MacEng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:54:53