当前位置:首页>python>Python爬虫新利器!Playwright助你一键获取豆瓣Top250完整数据

Python爬虫新利器!Playwright助你一键获取豆瓣Top250完整数据

  • 2026-02-05 01:06:43
Python爬虫新利器!Playwright助你一键获取豆瓣Top250完整数据

在网页自动化测试和爬虫领域,Selenium曾是无可争议的王者。但今天,我要向大家介绍一个更强大、更快速、更易用的替代品——Playwright。由Microsoft开发,支持所有主流浏览器,并且提供了同步和异步两种API,Playwright正在迅速成为开发者的新宠。

一、Playwright的优势亮点

1. 多浏览器支持

  • Chromium、Firefox、WebKit(Safari内核)全支持
  • 跨平台一致性,测试更可靠

2. 自动等待机制

  • 智能等待元素出现,告别time.sleep()
  • 内置大量等待条件,代码更简洁

3. 丰富的操作API

  • 文件上传、下载处理
  • 地理位置模拟、设备模拟
  • 截图、录屏功能

二、环境安装与配置

# 安装playwright库pip install playwright

三、快速上手:第一个Playwright脚本

from playwright.sync_api import sync_playwrightdefbaidu_search():with sync_playwright() as p:# 启动浏览器(默认Chromium)        browser = p.chromium.launch(headless=False)  # headless=False显示浏览器界面        page = browser.new_page()# 访问百度        page.goto("https://www.baidu.com")# 输入搜索关键词        page.fill("#kw""Playwright自动化")# 点击搜索按钮        page.click("#su")# 等待结果加载        page.wait_for_selector(".result.c-container", timeout=10000)# 截图保存        page.screenshot(path="search_result.png")# 获取搜索结果        results = page.query_selector_all(".result.c-container h3")for i, result inenumerate(results[:5], 1):            title = result.text_content()print(f"{i}{title}")# 关闭浏览器        browser.close()if __name__ == "__main__":    baidu_search()

四、核心功能详解

1. 元素定位与操作

# 多种定位方式page.click("text=登录")  # 按文本定位page.fill("#username""test_user")  # CSS选择器page.click("button:has-text('提交')")  # 组合选择器# XPath定位page.click("//button[@class='submit-btn']")# 处理iframeframe = page.frame_locator("iframe[name='content']")frame.locator("button").click()

2. 等待策略

# 多种等待方式page.wait_for_load_state("networkidle")  # 等待网络空闲page.wait_for_selector(".modal")  # 等待元素出现page.wait_for_timeout(3000)  # 强制等待(尽量避免使用)# 等待函数page.wait_for_function("""    () => {        return document.querySelectorAll('.item').length >= 10    }""")

3. 处理弹窗和对话框

# 监听对话框page.on("dialog"lambda dialog: dialog.accept())# 处理新窗口with page.expect_popup() as popup_info:    page.click("a[target='_blank']")new_page = popup_info.value

4. 文件上传和下载

# 文件上传page.set_input_files("input[type='file']""test.jpg")# 下载处理with page.expect_download() as download_info:    page.click("a.download-link")download = download_info.valuedownload.save_as("downloaded_file.pdf")

五、实战案例:豆瓣电影Top250爬虫

运行效果

import asyncioimport csvimport jsonimport osimport sysfrom dataclasses import dataclass, asdictfrom datetime import datetimefrom typing importListfrom playwright.async_api import async_playwrightimport re@dataclassclassMovie:    rank: int    title: str    rating: float    rating_count: str    directors: str    actors: str    year: str    country: str    tags: str    link: strclassConfig:"""配置文件"""# 自定义浏览器路径(设为None则使用默认)    CHROME_PATH = r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'# 你的路径# CHROME_PATH = None  # 使用Playwright自带的Chromium# 代理设置(如果需要)    PROXY = None# 例如: {'server': 'http://127.0.0.1:1080'}# 请求间隔(秒)    REQUEST_DELAY = 1# 输出目录    OUTPUT_DIR = 'douban_data'classDoubanScraper:def__init__(self, config: Config):self.config = configself.movies: List[Movie] = []# 确保输出目录存在        os.makedirs(config.OUTPUT_DIR, exist_ok=True)asyncdefcreate_browser(self):"""创建浏览器实例,支持自定义路径"""        p = await async_playwright().start()        launch_options = {'headless'False,  # 显示浏览器界面'slow_mo'100,     # 减慢操作速度,便于观察'args': ['--disable-blink-features=AutomationControlled','--start-maximized','--disable-notifications',            ]        }# 添加代理(如果配置了)ifself.config.PROXY:            launch_options['proxy'] = self.config.PROXY# 如果指定了Chrome路径,使用系统Chromeifself.config.CHROME_PATH and os.path.exists(self.config.CHROME_PATH):print(f"使用自定义Chrome: {self.config.CHROME_PATH}")            launch_options['executable_path'] = self.config.CHROME_PATH            launch_options['channel'] = 'chrome'else:print("使用Playwright自带Chromium")# 检查是否安装了Chromiumifnotself._check_chromium_installed():print("正在安装Chromium...")                os.system(f'{sys.executable} -m playwright install chromium')try:            browser = await p.chromium.launch(**launch_options)return p, browserexcept Exception as e:print(f"启动浏览器失败: {e}")# 尝试无头模式            launch_options['headless'] = True            browser = await p.chromium.launch(**launch_options)return p, browserdef_check_chromium_installed(self):"""检查Chromium是否已安装"""from playwright._impl._driver import get_driver_envimport jsontry:# 获取浏览器信息            env = get_driver_env()            browsers_file = os.path.join(env["PLAYWRIGHT_BROWSERS_PATH"], ".browsers.json")if os.path.exists(browsers_file):withopen(browsers_file, 'r'as f:                    browsers = json.load(f)returnany(b['name'] == 'chromium'for b in browsers['browsers'])except:passreturnFalseasyncdefscrape_movie_details(self, page, movie_link: str) -> dict:"""爬取电影详情页信息 - 修复版"""        details = {'directors''','actors''','year''','country''','tags''',  # 修改为tags,与Movie类字段名一致'duration'''        }try:# 创建新页面访问详情            context = page.context            detail_page = await context.new_page()print(f"   正在访问详情页: {movie_link}")await detail_page.goto(movie_link, wait_until='domcontentloaded')# 等待基本信息区域加载try:await detail_page.wait_for_selector('#info', timeout=10000)except:print("    详情页加载超时,跳过")await detail_page.close()return details# 获取整个信息区域的文本            info_text = await detail_page.locator('#info').text_content()            info_text = info_text.strip() if info_text else""# 提取导演信息try:# 方法1:使用更精确的选择器                director_elements = await detail_page.query_selector_all('#info span:has-text("导演") ~ span a')if director_elements:                    directors = []for elem in director_elements[:3]:  # 最多取3个导演                        name = await elem.text_content()if name:                            directors.append(name.strip())                    details['directors'] = ' / '.join(directors)else:# 如果上面的方法没找到,尝试另一种方式                    director_element = await detail_page.query_selector('#info span:has-text("导演")')if director_element:                        director_value_span = await director_element.query_selector('..')if director_value_span:# 获取下一个兄弟节点中的span                            next_span = await director_value_span.query_selector('following-sibling::span[1]')if next_span:                                director_text = await next_span.text_content()                                directors = director_text.split('/')                                details['directors'] = ' / '.join([d.strip() for d in directors[:3]])except Exception as e:print(f"   提取导演失败: {e}")# 提取演员信息(前5位)try:# 查找演员区域                actor_elements = await detail_page.query_selector_all('#info span:has-text("主演") ~ span a')if actor_elements:                    actors = []for actor_elem in actor_elements[:5]:  # 最多取5个演员                        actor_name = await actor_elem.text_content()if actor_name:                            actors.append(actor_name.strip())                    details['actors'] = ' / '.join(actors)else:# 如果上面的方法没找到,尝试另一种方式                    actor_element = await detail_page.query_selector('#info span:has-text("主演")')if actor_element:                        actor_value_span = await actor_element.query_selector('..')if actor_value_span:# 获取下一个兄弟节点中的span                            next_span = await actor_value_span.query_selector('following-sibling::span[1]')if next_span:                                actor_text = await next_span.text_content()                                actors = actor_text.split('/')                                details['actors'] = ' / '.join([a.strip() for a in actors[:5]])except Exception as e:print(f"   提取演员失败: {e}")# 提取年份(使用正则表达式)try:                year_match = re.search(r'(\d{4})', info_text)if year_match:                    details['year'] = year_match.group(1)except Exception as e:print(f"   提取年份失败: {e}")# 提取国家/地区try:if'制片国家/地区:'in info_text:                    country_part = info_text.split('制片国家/地区:')[1]# 取第一个换行符之前的内容                    country_text = country_part.split('\n')[0].strip()                    details['country'] = country_textexcept Exception as e:print(f"   提取国家失败: {e}")# 提取类型/标签(tags字段)try:# 方法1:使用属性选择器                genre_elements = await detail_page.query_selector_all('#info span[property="v:genre"]')if genre_elements:                    genres = []for elem in genre_elements:                        genre = await elem.text_content()if genre:                            genres.append(genre.strip())                    details['tags'] = ' / '.join(genres)  # 赋值给tags字段else:# 方法2:从文本中提取if'类型:'in info_text:                        genre_part = info_text.split('类型:')[1]                        genre_text = genre_part.split('\n')[0].strip()                        details['tags'] = genre_text  # 赋值给tags字段except Exception as e:print(f"   提取类型失败: {e}")# 提取时长(duration字段)try:                duration_elem = await detail_page.query_selector('#info span[property="v:runtime"]')if duration_elem:                    duration = await duration_elem.text_content()                    details['duration'] = duration.strip()else:# 尝试从文本中查找时长信息                    runtime_matches = re.findall(r'(\d+分钟|\d+集)', info_text)if runtime_matches:                        details['duration'] = ', '.join(runtime_matches)except Exception as e:print(f"   提取时长失败: {e}")await detail_page.close()except Exception as e:print(f"   爬取详情页失败: {e}")# 确保页面被关闭try:await detail_page.close()except:passreturn detailsasyncdefscrape(self):"""主爬取函数"""print("="*60)print("豆瓣电影Top250爬虫启动")print("="*60)        playwright, browser = awaitself.create_browser()try:            context = await browser.new_context(                viewport={'width'1920'height'1080},                user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ''(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',                locale='zh-CN',                timezone_id='Asia/Shanghai'            )# 拦截图片请求,加快速度await context.route("**/*.{png,jpg,jpeg}"lambda route: route.abort())            page = await context.new_page()for page_num inrange(10):  # 总共10页                start = page_num * 25                url = f'https://movie.douban.com/top250?start={start}&filter='print(f"\n📄 正在爬取第 {page_num + 1} 页...")await page.goto(url, wait_until='networkidle')# 等待电影列表加载await page.wait_for_selector('.grid_view .item', timeout=10000)# 获取当前页的所有电影项目                items = await page.locator('.grid_view .item').all()for item in items:try:# 基础信息                        rank = await item.locator('em').inner_text()                        title = await item.locator('.title').first.inner_text()# 处理可能的外文标题                        other_title_elem = item.locator('.other')ifawait other_title_elem.count() > 0:                            other_title = await other_title_elem.inner_text()                            title = f"{title}{other_title}"                        rating = await item.locator('.rating_num').inner_text()                        rating_count = await item.locator('span:nth-child(4)').inner_text()                        link_elem = item.locator('a').first                        movie_link = await link_elem.get_attribute('href')# 爬取详情信息print(f"  正在获取详情: {title}")                        details = awaitself.scrape_movie_details(page, movie_link)# 创建电影对象 - 修复:使用正确的字段名                        movie = Movie(                            rank=int(rank),                            title=title.strip(),                            rating=float(rating),                            rating_count=rating_count.strip(),                            directors=details['directors'],                            actors=details['actors'],                            year=details['year'],                            country=details['country'],                            tags=details['tags'],  # 修复:使用details['tags']而不是details['genres']                            link=movie_link                        )self.movies.append(movie)print(f"  ✓ #{movie.rank}{movie.title} ({movie.rating}分)")except Exception as e:print(f"  ✗ 解析电影失败: {e}")continueprint(f"✅ 第 {page_num + 1} 页完成,获取 {len(items)} 部电影")# 延迟await asyncio.sleep(self.config.REQUEST_DELAY)except Exception as e:print(f"爬取过程出错: {e}")finally:await browser.close()await playwright.stop()defsave_results(self):"""保存结果"""ifnotself.movies:print("没有数据可保存")return# 按排名排序self.movies.sort(key=lambda x: x.rank)        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")# 保存为CSV        csv_path = os.path.join(self.config.OUTPUT_DIR, f'douban_top250_{timestamp}.csv')withopen(csv_path, 'w', newline='', encoding='utf-8-sig'as f:            writer = csv.writer(f)            writer.writerow(['排名''标题''评分''评价人数''导演''主演''年份''国家''标签''链接'])for movie inself.movies:                writer.writerow([                    movie.rank,                    movie.title,                    movie.rating,                    movie.rating_count,                    movie.directors,                    movie.actors,                    movie.year,                    movie.country,                    movie.tags,  # 标签字段                    movie.link                ])# 保存为JSON        json_path = os.path.join(self.config.OUTPUT_DIR, f'douban_top250_{timestamp}.json')withopen(json_path, 'w', encoding='utf-8'as f:            data = {'scrape_time': datetime.now().isoformat(),'total'len(self.movies),'movies': [asdict(m) for m inself.movies]            }            json.dump(data, f, ensure_ascii=False, indent=2)# 打印统计信息print("\n" + "="*60)print("🎬 爬取完成!")print("="*60)print("📊 统计信息:")print(f"   总电影数: {len(self.movies)}")print(f"   最高评分: {max(m.rating for m in self.movies):.1f}")print(f"   平均评分: {sum(m.rating for m in self.movies)/len(self.movies):.2f}")print("\n💾 文件已保存:")print(f"   CSV文件: {csv_path}")print(f"   JSON文件: {json_path}")# 显示Top 5print("\n🏆 Top 5电影:")for movie inself.movies[:5]:print(f"   #{movie.rank} 《{movie.title}》 - {movie.rating}分")print(f"     导演: {movie.directors}")print(f"     类型: {movie.tags}")print("="*60)asyncdefmain():"""主函数"""# 配置    config = Config()# 创建爬虫并运行    scraper = DoubanScraper(config)await scraper.scrape()    scraper.save_results()if __name__ == "__main__":# Windows上需要设置事件循环策略if sys.platform == 'win32':        asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())# 运行主函数    asyncio.run(main())

六、高级技巧与优化

1. 性能优化

# 禁用图片加载,加速页面context = await browser.new_context(    viewport={"width"1920"height"1080},    user_agent="Mozilla/5.0...",    bypass_csp=True,  # 绕过内容安全策略    java_script_enabled=True,# 优化性能的选项    extra_http_headers={"Accept-Language""zh-CN,zh;q=0.9"    })# 拦截请求,只加载必要资源await page.route("**/*.{png,jpg,jpeg,gif,svg}"lambda route: route.abort())

2. 错误处理与重试

from tenacity import retry, stop_after_attempt, wait_exponential@retry(    stop=stop_after_attempt(3),    wait=wait_exponential(multiplier=1min=4max=10))asyncdefsafe_click(page, selector):try:await page.click(selector)except Exception as e:print(f"点击失败: {e}")raise

3. 分布式部署

# 使用playwright-core进行远程连接from playwright.sync_api import sync_playwrightdefconnect_to_remote_browser():with sync_playwright() as p:# 连接远程浏览器实例        browser = p.chromium.connect_over_cdp("http://remote-server:9222")        page = browser.new_page()# ... 后续操作

结语

Playwright作为新一代的浏览器自动化工具,以其强大的功能和优雅的API设计,正在改变自动化测试和网页爬虫的游戏规则。无论你是测试工程师、开发人员还是数据分析师,掌握Playwright都将为你的工作带来极大的便利。

学习资源推荐:

  • 官方文档[1]
  • Gitee仓库[2]
  • 中文社区[3]

引用链接

[1]官方文档: https://playwright.dev/python/

[2]Gitee仓库: https://gitee.com/fengde_jijie/python_spider

[3]中文社区: https://playwright.nodejs.cn/python/docs/intro

在网页自动化测试和爬虫领域,Selenium曾是无可争议的王者。但今天,我要向大家介绍一个更强大、更快速、更易用的替代品——Playwright。由Microsoft开发,支持所有主流浏览器,并且提供了同步和异步两种API,Playwright正在迅速成为开发者的新宠。

一、Playwright的优势亮点

1. 多浏览器支持

  • Chromium、Firefox、WebKit(Safari内核)全支持
  • 跨平台一致性,测试更可靠

2. 自动等待机制

  • 智能等待元素出现,告别time.sleep()
  • 内置大量等待条件,代码更简洁

3. 丰富的操作API

  • 文件上传、下载处理
  • 地理位置模拟、设备模拟
  • 截图、录屏功能

二、环境安装与配置

# 安装playwright库pip install playwright

三、快速上手:第一个Playwright脚本

from playwright.sync_api import sync_playwrightdefbaidu_search():with sync_playwright() as p:# 启动浏览器(默认Chromium)        browser = p.chromium.launch(headless=False)  # headless=False显示浏览器界面        page = browser.new_page()# 访问百度        page.goto("https://www.baidu.com")# 输入搜索关键词        page.fill("#kw""Playwright自动化")# 点击搜索按钮        page.click("#su")# 等待结果加载        page.wait_for_selector(".result.c-container", timeout=10000)# 截图保存        page.screenshot(path="search_result.png")# 获取搜索结果        results = page.query_selector_all(".result.c-container h3")for i, result inenumerate(results[:5], 1):            title = result.text_content()print(f"{i}{title}")# 关闭浏览器        browser.close()if __name__ == "__main__":    baidu_search()

四、核心功能详解

1. 元素定位与操作

# 多种定位方式page.click("text=登录")  # 按文本定位page.fill("#username""test_user")  # CSS选择器page.click("button:has-text('提交')")  # 组合选择器# XPath定位page.click("//button[@class='submit-btn']")# 处理iframeframe = page.frame_locator("iframe[name='content']")frame.locator("button").click()

2. 等待策略

# 多种等待方式page.wait_for_load_state("networkidle")  # 等待网络空闲page.wait_for_selector(".modal")  # 等待元素出现page.wait_for_timeout(3000)  # 强制等待(尽量避免使用)# 等待函数page.wait_for_function("""    () => {        return document.querySelectorAll('.item').length >= 10    }""")

3. 处理弹窗和对话框

# 监听对话框page.on("dialog"lambda dialog: dialog.accept())# 处理新窗口with page.expect_popup() as popup_info:    page.click("a[target='_blank']")new_page = popup_info.value

4. 文件上传和下载

# 文件上传page.set_input_files("input[type='file']""test.jpg")# 下载处理with page.expect_download() as download_info:    page.click("a.download-link")download = download_info.valuedownload.save_as("downloaded_file.pdf")

五、实战案例:豆瓣电影Top250爬虫

运行效果

import asyncioimport csvimport jsonimport osimport sysfrom dataclasses import dataclass, asdictfrom datetime import datetimefrom typing importListfrom playwright.async_api import async_playwrightimport re@dataclassclassMovie:    rank: int    title: str    rating: float    rating_count: str    directors: str    actors: str    year: str    country: str    tags: str    link: strclassConfig:"""配置文件"""# 自定义浏览器路径(设为None则使用默认)    CHROME_PATH = r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'# 你的路径# CHROME_PATH = None  # 使用Playwright自带的Chromium# 代理设置(如果需要)    PROXY = None# 例如: {'server': 'http://127.0.0.1:1080'}# 请求间隔(秒)    REQUEST_DELAY = 1# 输出目录    OUTPUT_DIR = 'douban_data'classDoubanScraper:def__init__(self, config: Config):self.config = configself.movies: List[Movie] = []# 确保输出目录存在        os.makedirs(config.OUTPUT_DIR, exist_ok=True)asyncdefcreate_browser(self):"""创建浏览器实例,支持自定义路径"""        p = await async_playwright().start()        launch_options = {'headless'False,  # 显示浏览器界面'slow_mo'100,     # 减慢操作速度,便于观察'args': ['--disable-blink-features=AutomationControlled','--start-maximized','--disable-notifications',            ]        }# 添加代理(如果配置了)ifself.config.PROXY:            launch_options['proxy'] = self.config.PROXY# 如果指定了Chrome路径,使用系统Chromeifself.config.CHROME_PATH and os.path.exists(self.config.CHROME_PATH):print(f"使用自定义Chrome: {self.config.CHROME_PATH}")            launch_options['executable_path'] = self.config.CHROME_PATH            launch_options['channel'] = 'chrome'else:print("使用Playwright自带Chromium")# 检查是否安装了Chromiumifnotself._check_chromium_installed():print("正在安装Chromium...")                os.system(f'{sys.executable} -m playwright install chromium')try:            browser = await p.chromium.launch(**launch_options)return p, browserexcept Exception as e:print(f"启动浏览器失败: {e}")# 尝试无头模式            launch_options['headless'] = True            browser = await p.chromium.launch(**launch_options)return p, browserdef_check_chromium_installed(self):"""检查Chromium是否已安装"""from playwright._impl._driver import get_driver_envimport jsontry:# 获取浏览器信息            env = get_driver_env()            browsers_file = os.path.join(env["PLAYWRIGHT_BROWSERS_PATH"], ".browsers.json")if os.path.exists(browsers_file):withopen(browsers_file, 'r'as f:                    browsers = json.load(f)returnany(b['name'] == 'chromium'for b in browsers['browsers'])except:passreturnFalseasyncdefscrape_movie_details(self, page, movie_link: str) -> dict:"""爬取电影详情页信息 - 修复版"""        details = {'directors''','actors''','year''','country''','tags''',  # 修改为tags,与Movie类字段名一致'duration'''        }try:# 创建新页面访问详情            context = page.context            detail_page = await context.new_page()print(f"   正在访问详情页: {movie_link}")await detail_page.goto(movie_link, wait_until='domcontentloaded')# 等待基本信息区域加载try:await detail_page.wait_for_selector('#info', timeout=10000)except:print("    详情页加载超时,跳过")await detail_page.close()return details# 获取整个信息区域的文本            info_text = await detail_page.locator('#info').text_content()            info_text = info_text.strip() if info_text else""# 提取导演信息try:# 方法1:使用更精确的选择器                director_elements = await detail_page.query_selector_all('#info span:has-text("导演") ~ span a')if director_elements:                    directors = []for elem in director_elements[:3]:  # 最多取3个导演                        name = await elem.text_content()if name:                            directors.append(name.strip())                    details['directors'] = ' / '.join(directors)else:# 如果上面的方法没找到,尝试另一种方式                    director_element = await detail_page.query_selector('#info span:has-text("导演")')if director_element:                        director_value_span = await director_element.query_selector('..')if director_value_span:# 获取下一个兄弟节点中的span                            next_span = await director_value_span.query_selector('following-sibling::span[1]')if next_span:                                director_text = await next_span.text_content()                                directors = director_text.split('/')                                details['directors'] = ' / '.join([d.strip() for d in directors[:3]])except Exception as e:print(f"   提取导演失败: {e}")# 提取演员信息(前5位)try:# 查找演员区域                actor_elements = await detail_page.query_selector_all('#info span:has-text("主演") ~ span a')if actor_elements:                    actors = []for actor_elem in actor_elements[:5]:  # 最多取5个演员                        actor_name = await actor_elem.text_content()if actor_name:                            actors.append(actor_name.strip())                    details['actors'] = ' / '.join(actors)else:# 如果上面的方法没找到,尝试另一种方式                    actor_element = await detail_page.query_selector('#info span:has-text("主演")')if actor_element:                        actor_value_span = await actor_element.query_selector('..')if actor_value_span:# 获取下一个兄弟节点中的span                            next_span = await actor_value_span.query_selector('following-sibling::span[1]')if next_span:                                actor_text = await next_span.text_content()                                actors = actor_text.split('/')                                details['actors'] = ' / '.join([a.strip() for a in actors[:5]])except Exception as e:print(f"   提取演员失败: {e}")# 提取年份(使用正则表达式)try:                year_match = re.search(r'(\d{4})', info_text)if year_match:                    details['year'] = year_match.group(1)except Exception as e:print(f"   提取年份失败: {e}")# 提取国家/地区try:if'制片国家/地区:'in info_text:                    country_part = info_text.split('制片国家/地区:')[1]# 取第一个换行符之前的内容                    country_text = country_part.split('\n')[0].strip()                    details['country'] = country_textexcept Exception as e:print(f"   提取国家失败: {e}")# 提取类型/标签(tags字段)try:# 方法1:使用属性选择器                genre_elements = await detail_page.query_selector_all('#info span[property="v:genre"]')if genre_elements:                    genres = []for elem in genre_elements:                        genre = await elem.text_content()if genre:                            genres.append(genre.strip())                    details['tags'] = ' / '.join(genres)  # 赋值给tags字段else:# 方法2:从文本中提取if'类型:'in info_text:                        genre_part = info_text.split('类型:')[1]                        genre_text = genre_part.split('\n')[0].strip()                        details['tags'] = genre_text  # 赋值给tags字段except Exception as e:print(f"   提取类型失败: {e}")# 提取时长(duration字段)try:                duration_elem = await detail_page.query_selector('#info span[property="v:runtime"]')if duration_elem:                    duration = await duration_elem.text_content()                    details['duration'] = duration.strip()else:# 尝试从文本中查找时长信息                    runtime_matches = re.findall(r'(\d+分钟|\d+集)', info_text)if runtime_matches:                        details['duration'] = ', '.join(runtime_matches)except Exception as e:print(f"   提取时长失败: {e}")await detail_page.close()except Exception as e:print(f"   爬取详情页失败: {e}")# 确保页面被关闭try:await detail_page.close()except:passreturn detailsasyncdefscrape(self):"""主爬取函数"""print("="*60)print("豆瓣电影Top250爬虫启动")print("="*60)        playwright, browser = awaitself.create_browser()try:            context = await browser.new_context(                viewport={'width'1920'height'1080},                user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ''(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',                locale='zh-CN',                timezone_id='Asia/Shanghai'            )# 拦截图片请求,加快速度await context.route("**/*.{png,jpg,jpeg}"lambda route: route.abort())            page = await context.new_page()for page_num inrange(10):  # 总共10页                start = page_num * 25                url = f'https://movie.douban.com/top250?start={start}&filter='print(f"\n📄 正在爬取第 {page_num + 1} 页...")await page.goto(url, wait_until='networkidle')# 等待电影列表加载await page.wait_for_selector('.grid_view .item', timeout=10000)# 获取当前页的所有电影项目                items = await page.locator('.grid_view .item').all()for item in items:try:# 基础信息                        rank = await item.locator('em').inner_text()                        title = await item.locator('.title').first.inner_text()# 处理可能的外文标题                        other_title_elem = item.locator('.other')ifawait other_title_elem.count() > 0:                            other_title = await other_title_elem.inner_text()                            title = f"{title}{other_title}"                        rating = await item.locator('.rating_num').inner_text()                        rating_count = await item.locator('span:nth-child(4)').inner_text()                        link_elem = item.locator('a').first                        movie_link = await link_elem.get_attribute('href')# 爬取详情信息print(f"  正在获取详情: {title}")                        details = awaitself.scrape_movie_details(page, movie_link)# 创建电影对象 - 修复:使用正确的字段名                        movie = Movie(                            rank=int(rank),                            title=title.strip(),                            rating=float(rating),                            rating_count=rating_count.strip(),                            directors=details['directors'],                            actors=details['actors'],                            year=details['year'],                            country=details['country'],                            tags=details['tags'],  # 修复:使用details['tags']而不是details['genres']                            link=movie_link                        )self.movies.append(movie)print(f"  ✓ #{movie.rank}{movie.title} ({movie.rating}分)")except Exception as e:print(f"  ✗ 解析电影失败: {e}")continueprint(f"✅ 第 {page_num + 1} 页完成,获取 {len(items)} 部电影")# 延迟await asyncio.sleep(self.config.REQUEST_DELAY)except Exception as e:print(f"爬取过程出错: {e}")finally:await browser.close()await playwright.stop()defsave_results(self):"""保存结果"""ifnotself.movies:print("没有数据可保存")return# 按排名排序self.movies.sort(key=lambda x: x.rank)        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")# 保存为CSV        csv_path = os.path.join(self.config.OUTPUT_DIR, f'douban_top250_{timestamp}.csv')withopen(csv_path, 'w', newline='', encoding='utf-8-sig'as f:            writer = csv.writer(f)            writer.writerow(['排名''标题''评分''评价人数''导演''主演''年份''国家''标签''链接'])for movie inself.movies:                writer.writerow([                    movie.rank,                    movie.title,                    movie.rating,                    movie.rating_count,                    movie.directors,                    movie.actors,                    movie.year,                    movie.country,                    movie.tags,  # 标签字段                    movie.link                ])# 保存为JSON        json_path = os.path.join(self.config.OUTPUT_DIR, f'douban_top250_{timestamp}.json')withopen(json_path, 'w', encoding='utf-8'as f:            data = {'scrape_time': datetime.now().isoformat(),'total'len(self.movies),'movies': [asdict(m) for m inself.movies]            }            json.dump(data, f, ensure_ascii=False, indent=2)# 打印统计信息print("\n" + "="*60)print("🎬 爬取完成!")print("="*60)print("📊 统计信息:")print(f"   总电影数: {len(self.movies)}")print(f"   最高评分: {max(m.rating for m in self.movies):.1f}")print(f"   平均评分: {sum(m.rating for m in self.movies)/len(self.movies):.2f}")print("\n💾 文件已保存:")print(f"   CSV文件: {csv_path}")print(f"   JSON文件: {json_path}")# 显示Top 5print("\n🏆 Top 5电影:")for movie inself.movies[:5]:print(f"   #{movie.rank} 《{movie.title}》 - {movie.rating}分")print(f"     导演: {movie.directors}")print(f"     类型: {movie.tags}")print("="*60)asyncdefmain():"""主函数"""# 配置    config = Config()# 创建爬虫并运行    scraper = DoubanScraper(config)await scraper.scrape()    scraper.save_results()if __name__ == "__main__":# Windows上需要设置事件循环策略if sys.platform == 'win32':        asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())# 运行主函数    asyncio.run(main())

六、高级技巧与优化

1. 性能优化

# 禁用图片加载,加速页面context = await browser.new_context(    viewport={"width"1920"height"1080},    user_agent="Mozilla/5.0...",    bypass_csp=True,  # 绕过内容安全策略    java_script_enabled=True,# 优化性能的选项    extra_http_headers={"Accept-Language""zh-CN,zh;q=0.9"    })# 拦截请求,只加载必要资源await page.route("**/*.{png,jpg,jpeg,gif,svg}"lambda route: route.abort())

2. 错误处理与重试

from tenacity import retry, stop_after_attempt, wait_exponential@retry(    stop=stop_after_attempt(3),    wait=wait_exponential(multiplier=1min=4max=10))asyncdefsafe_click(page, selector):try:await page.click(selector)except Exception as e:print(f"点击失败: {e}")raise

3. 分布式部署

# 使用playwright-core进行远程连接from playwright.sync_api import sync_playwrightdefconnect_to_remote_browser():with sync_playwright() as p:# 连接远程浏览器实例        browser = p.chromium.connect_over_cdp("http://remote-server:9222")        page = browser.new_page()# ... 后续操作

结语

Playwright作为新一代的浏览器自动化工具,以其强大的功能和优雅的API设计,正在改变自动化测试和网页爬虫的游戏规则。无论你是测试工程师、开发人员还是数据分析师,掌握Playwright都将为你的工作带来极大的便利。

学习资源推荐:

  • 官方文档[1]
  • Gitee仓库[2]
  • 中文社区[3]

引用链接

[1]官方文档: https://playwright.dev/python/

[2]Gitee仓库: https://gitee.com/fengde_jijie/python_spider

[3]中文社区: https://playwright.nodejs.cn/python/docs/intro

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-07 21:47:11 HTTP/2.0 GET : https://f.mffb.com.cn/a/471941.html
  2. 运行时间 : 0.228650s [ 吞吐率:4.37req/s ] 内存消耗:4,682.55kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=f57b8d6b3f54abe6855cd64e92693989
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000437s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000818s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.006961s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000688s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000452s ]
  6. SELECT * FROM `set` [ RunTime:0.008211s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000644s ]
  8. SELECT * FROM `article` WHERE `id` = 471941 LIMIT 1 [ RunTime:0.012604s ]
  9. UPDATE `article` SET `lasttime` = 1770472031 WHERE `id` = 471941 [ RunTime:0.000616s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000380s ]
  11. SELECT * FROM `article` WHERE `id` < 471941 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001226s ]
  12. SELECT * FROM `article` WHERE `id` > 471941 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.022358s ]
  13. SELECT * FROM `article` WHERE `id` < 471941 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.046575s ]
  14. SELECT * FROM `article` WHERE `id` < 471941 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.031201s ]
  15. SELECT * FROM `article` WHERE `id` < 471941 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.022536s ]
0.231069s