得有两个月没记笔记了,懒是一方面,主要还是天天这部那局的需求不断,劳心费力呀,也没得啥时间;最近给业务部门做了一个简单的爬虫项目,使用的是selenium4,需求很简单,但到了桃宝,你东上,面对复杂的元素结构,有些数据还是蛮难取的,记录一下踩过的坑点:

1、防爬虫绕过

参考https://www.jianshu.com/p/368be2cc6ca1,这里的解决方案是加参和修改chromdriver.exe的驱动内字段:

        option = ChromeOptions()
        '''
        规避检测参数
        最好更改谷歌浏览器驱动这个值 $cdc_asdjflasutopfhvcZLmcfl(注意要等长) 
        不被检测出来的话,一般是不用滑块验证的
        阿里的滑块通过率不高,比较麻烦
        '''

        option.add_argument("--disable-blink-features=AutomationControlled")
        option.add_experimental_option('excludeSwitches', ['enable-automation'])


        # 实例化浏览器对象

        driver = webdriver.Chrome(service=Service(r'C:\Program Files\Google\Chrome\Application\chromedriver.exe'))

        driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                       Object.defineProperty(navigator, 'webdriver', {
                         get: () => undefined
                       })
                     """
        })

2、一些Selenium4的基本操作

推荐边看边做:https://www.byhy.net/

比如获取元素路径写法、显式(隐式)等待写法,都是和selenium3有所不同的:

        driver.find_element(By.XPATH,'//a[@class = "h"]').click()
        time.sleep(1)

        #切换扫码登录 中间需要1-2步人工确认,所以等待15s
        driver.find_element(By.XPATH,'//i[@class = "iconfont icon-qrcode"]').click()
        time.sleep(15)

鼠标移动到元素上并悬停:

        from selenium.webdriver.common.action_chains import ActionChains

        ac = ActionChains(driver)

        # 鼠标移动到 元素上
        ac.move_to_element(
            driver.find_element(By.XPATH, '//*[@id="J_PriceStart"]')
        ).perform()
        time.sleep(5)

切换窗口:

        # 切换窗口
        driver.switch_to.window(driver.window_handles[-1])
        time.sleep(1)

保存当前窗口句柄:

        # mainWindow变量保存当前窗口的句柄
        mainWindow = driver.current_window_handle

切换至新窗口并切换句柄,这样driver才能在新页面获取元素:

                            # WebDriver对象有window_handles 属性,这是一个列表对象, 里面包括了当前浏览器里面所有的窗口句柄。
                            # 所谓句柄,大家可以想象成对应网页窗口的一个ID
                            # 切换到新窗口操作  handle:窗口句柄
                            for handle in driver.window_handles:
                                # 先切换到该窗口
                                driver.switch_to.window(handle)
                                # 得到该窗口的标题栏字符串,判断是不是我们要操作的那个窗口
                                if href_text in driver.current_url:
                                    # 如果是,那么这时候WebDriver对象就是对应的该该窗口,正好,跳出循环,
                                    break
                            time.sleep(1)

返回老句柄:

                            # 通过前面保存的老窗口的句柄,自己切换到老窗口
                            driver.switch_to.window(mainWindow)

3、find_elements 和 find_element

后者好理解,就是找元素用的方法,前者一般在遍历列表、确认业务逻辑是否能翻页时用到,比如:

li_list = driver.find_elements(By.XPATH, '//div[@class="sf-content"]/div[@class="sf-item-list"]/ul/li')
                time.sleep(1)
                print(len(li_list))
                if len(li_list) > 0:
                    for li in li_list:
                        title = li.find_element(By.XPATH, './a/div[1]/p').text
                        dq_price = li.find_element(By.XPATH, './a/div[2]/p[{}]'.format(dq_x)).text

xxx 其实确认翻页也不一定用确认该elements下元素个数来决定是否翻页,还是需要根据页面给的元素场景来,比如桃宝我就直接定位了某一翻页元素的值,从而确定是否需要翻页。

4、有时会报找不到元素的错误

改成如下方法:

            # td.click()
            #0610报找不到元素坐标错,好奇怪,不知道是不是102的chromedriver不稳定,改成下面方法
            driver.execute_script("arguments[0].click()", td)

5、千人千面的详细信息数据获取

元素标签千奇百怪,相同标签在这位置,有的在那位置,有的甚至没有,给自动化起了很大的阻碍作用,为了做到相对统一我甚至:

1.PNG

狗看了狗都死了,最后算是做到了相对统一的结果。。。

6、表格记录

用到了openxyl,网上一搜一堆

主要就是创建sheet,写入表头,写入数据,追加sheet等,还是要基于自己的业务逻辑来写入(无非就是大段大段的条件判断、循环处理)

7、截图

直接扔网上的轮子:

def capture_full_screen(driver) :

    def send(cmd, params):
        resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
        url = driver.command_executor._url + resource
        body = json.dumps({"cmd":cmd, "params": params})
        response = driver.command_executor._request("POST", url, body)
        return response.get("value")

    def evaluate(script):
        response = send("Runtime.evaluate", {"returnByValue": True, "expression": script})
        return response["result"]["value"]

    metrics = evaluate(\
    "({" + \
      "width: Math.max(window.innerWidth, document.body.scrollWidth, document.documentElement.scrollWidth)|0," + \
      "height: Math.max(innerHeight, document.body.scrollHeight, document.documentElement.scrollHeight)|0," + \
      "deviceScaleFactor: window.devicePixelRatio || 1," + \
      "mobile: typeof window.orientation !== 'undefined'" + \
    "})")
    send("Emulation.setDeviceMetricsOverride", metrics)
    screenshot = send("Page.captureScreenshot", {"format": "png", "fromSurface": True})
    send("Emulation.clearDeviceMetricsOverride", {})

    return base64.b64decode(screenshot["data"])

8、取完数据后的表格美化(去空行)

        wb = openpyxl.load_workbook(r'D:\\1.xlsx')
        ws = wb.active
        row = ws.max_row
        column = ws.max_column
        # 从大到小迭代
        for x in range(row, 1, -1):
            s = ws.cell(x, column).value
            if s is None:
                ws.delete_rows(x)
            else:
                pass
        wb.save(r'D:\\1.xlsx')

ex、处理数据的正则、切片、随机数、写文件、替换等

    stock_code = []

    with open(r'D:\\2.txt', 'r') as f:
        for item in f:
            item = item[:-3].replace('.', '')
            stock_code.append(item)

    with open(r'D:\\' + status_text + '_' + re_title + '_' + serno + r'.html','wb') as f:
         f.write(inner_html.encode('utf-8'))

    import re
    str = re.sub('[^\u4e00-\u9fa5]+','',confirm_content_price)
    print(str)
    print(str[:4]+':'+str[4:])

    # 生成随机数
    serno = str(random.randint(100000000, 999999999))

要涉及到业务逻辑,主要还是以Selenium为主,一些基础方法为辅,简单记录下备忘。

0706关于iframe的新坑

面对iframe的场景,需要在循环最后再切回原iframe,要不回原页面循环会报:element is not attached to the page document错误

需要在在开新窗口取数循环体的结构里做如下操作:

循环外先定位到对应的iframe:
        iframe = driver.find_element(By.XPATH,'//*[@class = "ifr01"]')
        driver.switch_to.frame(iframe)
进循环切新页面后,在关闭新页面并返回原句柄的同时,也需要切回下原iframe:
        # 关闭当前页面
         driver.close()
        # 通过前面保存的老窗口的句柄,自己切换到老窗口
        driver.switch_to.window(mainWindow)
        time.sleep(1)

        # 面对iframe的场景,需要在循环最后再切回原iframe,要不回原页面循环会报:
        # element is not attached to the page document错误
        iframe = driver.find_element(By.XPATH, '//*[@class = "ifr01"]')
        driver.switch_to.frame(iframe)


标题:用Selenium4做一个简单的爬虫项目
作者:jyl
地址:http://114.116.238.43/articles/2022/07/05/1657031406808.html