WebKit之Page Cache

本文深入探讨WebKit中的PageCache机制,包括Page与Frame的关系、HistoryItem的缓存原理及管理方式,以及CachedPage如何缓存Document等关键对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

WebKit之Page Cache

Page、Frame

一个Page可以显示多个web页面,每个页面都有一个main frame,main frame可以有子frame,frame是不共享的
如此一个Page有一个BackForwardList,有若干个HistoryItem,每个HistoryItem都有一个CachedPage


在WebKit中所有可以并且缓存了的Page都记录在Page Cache中
在Page Cache中缓存的是HistoryItem对象,使用LRU链表将所有缓存的HistoryItem保存起来
当要清除某个HistoryItem在PageCache中有定时器进行辅助清理

实际上真正缓存对象的是CachedPage,而对缓存的管理对象是HistoryItem,所有的HistoryItem存储在BackForwardList中

CachedPage主要缓存了CachedFrame对象,CachedFrame主要缓存了Page的几个主要对象:Document、DocumentLoader和FrameView等

当资源加载完毕调用FrameLoader::commitProvisionalLoad时会进行Page Cache操作:
1. 如果可以缓存当前页面且当前页面没有缓存,调用cachePageForHistoryItem缓存当前页面,即添加到PageCache类中,只有主Frame对应的HistoryItem才会加入PageCache类中
2. 在transitionToCommitted中,对于新加载的页面会创建HitoryItem树(按照主frame与子frame的关系)
3. 如果是加载自Page Cache,在调用open从Page Cache中加载
void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)
{
    RefPtr<CachedPage> cachedPage = prpCachedPage;
    ......
    if (canCachePage() && !m_currentHistoryItem->isInPageCache()) {
        if (Document* document = m_frame->document())
            document->suspendActiveDOMObjects();
        cachePageForHistoryItem(m_currentHistoryItem.get());
    }
    ......
    transitionToCommitted(cachedPage);
    ......
    if (cachedPage && cachedPage->document()) {
        open(*cachedPage);
        ......
    } else {       
        ......
    }
    ......
    opened();
}

""" @author: jtahstu @contact: root@jtahstu.com @site: http://www.jtahstu.com @time: 2017/12/10 00:25 """ # -*- coding: utf-8 -*- import requests from bs4 import BeautifulSoup import time from pymongo import MongoClient headers = { 'x-devtools-emulate-network-conditions-client-id': "5f2fc4da-c727-43c0-aad4-37fce8e3ff39", 'upgrade-insecure-requests': "1", 'user-agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36", 'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", 'dnt': "1", 'accept-encoding': "gzip, deflate", 'accept-language': "zh-CN,zh;q=0.8,en;q=0.6", 'cookie': "__c=1501326829; lastCity=101020100; __g=-; __l=r=https%3A%2F%2Fwww.google.com.hk%2F&l=%2F; __a=38940428.1501326829..1501326829.20.1.20.20; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1501326839; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1502948718; __c=1501326829; lastCity=101020100; __g=-; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1501326839; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1502954829; __l=r=https%3A%2F%2Fwww.google.com.hk%2F&l=%2F; __a=38940428.1501326829..1501326829.21.1.21.21", 'cache-control': "no-cache", 'postman-token': "76554687-c4df-0c17-7cc0-5bf3845c9831" } conn = MongoClient('127.0.0.1', 27017) db = conn.iApp # 连接mydb数据库,没有则自动创建 def init(): items = db.jobs_php.find().sort('pid') for item in items: if 'detail' in item.keys(): # 在爬虫挂掉再此爬取时,跳过已爬取的行 continue detail_url = "https://www.zhipin.com/job_detail/%s.html?ka=search_list_1" % item['pid'] print(detail_url) html = requests.get(detail_url, headers=headers) if html.status_code != 200: # 爬的太快网站返回403,这时等待解封吧 print('status_code is %d' % html.status_code) break soup = BeautifulSoup(html.text, "html.parser") job = soup.select(".job-sec .text") if len(job) < 1:
最新发布
03-18
### BeautifulSoup 解析结果为空的原因分析 当使用 `BeautifulSoup` 进行 HTML 页面解析时,如果返回的结果为空(即 `len(job) < 1`),可能由以下几个原因引起: #### 1. **目标网站的反爬机制** 许多现代网站为了防止被恶意爬虫访问,会设置各种反爬措施。例如,动态加载内容、验证请求头或 IP 地址等。这可能导致直接获取到的内容不完整或者完全无法获取所需的数据[^3]。 #### 2. **HTML 结构变化** 目标网页的 HTML 结构可能会随着时间更新而发生变化。如果原始代码基于旧版结构编写,则新的标签名、类名或其他属性的变化都会导致定位不到预期的目标节点[^2]。 #### 3. **JavaScript 动态渲染** 部分网页内容并非静态存在于初始 HTML 文件中,而是通过 JavaScript 脚本在客户端执行后生成并注入 DOM 树内的。这种情况下仅依靠简单的 HTTP 请求下载下来的 HTML 文档并不包含最终展示给用户的全部信息。 #### 4. **网络连接问题或超时错误** 在网络状况不佳的情况下也可能发生这种情况——即使 URL 是有效的但由于某些临时性的网络障碍使得服务器未能成功响应完整的页面资源[^1]。 --- ### 针对上述情况的具体解决方案如下所示: #### 方法一:确认正确的选择器路径 重新审查所使用的 CSS Selector 或 XPath 是否仍然匹配当前最新的 HTML 片段布局;可以通过浏览器开发者工具查看最新实际呈现出来的标记名称及其对应的 class/id 属性值来调整查询表达式。 ```python from bs4 import BeautifulSoup as soup html_content = "<div id='main'><span>Hello</span></div>" bs_obj = soup(html_content, 'html.parser') result = bs_obj.select("#main span") # 正确的选择器应该能捕获到 "Hello" if not result: print("未找到任何元素") else: for item in result: print(item.text.strip()) ``` #### 方法二:启用 Selenium 处理 JS 渲染后的页面 对于那些依赖于前端脚本来构建显示界面的部分站点来说单纯利用 requests 库不足以满足需求,这时就需要引入像 selenium 这样的自动化测试框架配合真实浏览器实例完成整个交互过程后再提取所需的 html 数据。 ```python from selenium import webdriver import time driver_path = '/path/to/chromedriver' options = webdriver.ChromeOptions() options.add_argument('--headless') browser = webdriver.Chrome(executable_path=driver_path,options=options) url="https://example.com/jobs" try: browser.get(url) time.sleep(5) # 等待js加载完毕 page_source=browser.page_source finally: browser.quit() parsed_html=soup(page_source,'lxml') jobs=parsed_html.find_all('div',{'class':'job-item'}) print(f"共发现 {len(jobs)} 条职位记录.") for job in jobs[:min(len(jobs),5)] : title_tag=job.find('h2', {'class': 'title'}).a company_name=job.find('span',{'itemprop':'name'}).text location=job.find('small').text.split('-')[0].strip() print(f"{company_name}正在招聘{title_tag['href']}位于{location}") ``` #### 方法三:增加 headers 和 cookies 支持 一些服务端会对 User-Agent 字符串以及其他头部字段做校验,因此建议模仿真实的用户代理发送请求,并附加必要的 cookie 参数以绕过基本的身份认证屏障。 ```python import requests headers={ 'User-Agent':"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98 Safari/537.36", } cookies={'sessionid':'your_session_id'} response=requests.get(url=url,headers=headers,cookies=cookies).content.decode('utf-8') soup_instance=soup(response,"html.parser") elements=soup_instance.findAll('tag-name',{attribute:value}) assert len(elements)>0 , f"No elements found with given criteria!" ``` #### 方法四:重试逻辑与异常捕捉 考虑到偶尔会出现因网络波动引起的失败情形,在实现过程中加入合理的重试次数以及详细的日志跟踪有助于提升整体稳定性^. ```python def fetch_data_with_retry(max_retries:int,url:str,**kwargs)->str|None: attempt_count=0 while True: try: resp=requests.request(**kwargs) if resp.status_code==requests.codes.okay and '<!--anti-bot-->'not in str(resp.content): return resp.text raise ValueError(f'Received unexpected status code:{resp.status_code}') except Exception as e: logging.error(e.args[0]) attempt_count+=1 if max_retries<=attempt_count: break return None ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值