http://blog.sina.com.cn/s/blog_a573f7990101dm3b.html

原文连接:http://blog.sina.com.cn/s/blog_a573f7990101dm3b.html

一、应用图片

标准iOS控件里的图片资源,苹果已经做了相应的升级,我们需要操心的是应用自己的图片资源。

就像当初为了支持iPhone 4而制作的@2x高分辨率版本(译者:以下简称高分)图片一样,

我们要为iPad应用中的图片制作对应的高分版本。

我知道不少开发者很有预见性的早在iOS 5.0 SDK上就完成了这一步升级。

可是我还是要强调一点,那就是之前 Michael Jurewitz (@Jury)在推上提到过的:

— 如果想让你的高分图片显示在新iPad上,你必须用Xcode 4.3.1 连同iOS 5.1 SDK编译!


二、应用图标

接下来肯定就是为应用主图标制作高分版本了,因为低分版的图标在新iPad桌面上看起来会惨不忍睹。

为了支持更多的iOS设备、更高分辨率的屏幕,iOS开发者需要为自己的应用准备各种尺寸的主程序图标文件,

而且这个文件列表貌似会越来越长越来越长……(译者:叹气~)。

详情请参考最新的苹果开发者文档 iOS App Programming Guide 和 iOS Human Interface Guidelines 。

从官方文档中,我找出来针对新iPad的Retina显示屏我们需要准备的东西:

iPad主应用图标 (144×144像素):之前用在iPad 1、2代上的是72×72 像素。

现在我们需要额外的@2x版本(144×144 像素)。

iPad搜索结果图标 (100×100像素):这个图标出现在系统搜索结果中(译者注:还有在系统设置中,如果应用支持的话)。

之前版本用的是50×50像素,现在@2x版本需要100×100像素。


文件命名和 Info.plist 文件:

根据你的应用需要支持的iOS最低版本不同,你可能需要在Info.plist文件中指定图标文件名,

或者是按照苹果的规范命名不同版本的主图标文件。

最悲催的情况恐怕就是,你搞的是一个即可跑在iPhone也可跑在iPad上的通用应用(universal app),

并且你打算支持iOS 3.1.x甚至更早的版本

(译者:其实现在iOS 4.0及以上版本的设备普及率已经很高了,完全没有必要支持古董级的版本,咱又不是Android)。

因为iOS 3.2之前是不支持在Info.plist文件里面指定图标文件的,所以你得使用苹果指定的规范去命名图标文件。

一个完整的列表差不多就是下面这个样子:

● Icon.png – 57×57 iPhone应用图标

● Icon@2x.png – 114×114 iPhone Retina显示屏应用图标

● Icon-72.png – 72×72 iPad应用图标

● Icon-72@2x.png  - 144×144 iPad Retina显示屏应用图标

● Icon-Small.png – 29×29 iPhone 系统设置和搜索结果图标

● Icon-Small@2x.png – 58×58 iPhone Retina显示屏 系统设置和搜索结果图标

● Icon-Small-50.png – 50×50 iPad 系统设置和搜索结果图标

● Icon-Small-50@2x.png – 100×100 iPad Retina显示屏 系统设置和搜索结果图标

如果你的应用仅兼容iOS 3.2及之后的版本,那么你可以在Info.plist文件里面指定图标文件,

你不用遵守上面的命名规范,当然你非要那么命名也没有问题

(译者:为了便于和美工沟通和日后项目资源的管理,还是建议遵循这套规范)。

在iOS 3.2中,苹果在Info.plist文件中引入了CFBundleIconFiles键,在此其中你可以直接指定应用图标的各种版本。

如果你忽略了.png的后缀名,那么你也可以忽略高分版本图片的@2x部分,系统会自动匹配。


苹果在iOS 5.0中为了支持报刊杂志(Newsstand)功能,再次引入了一个新的键 CFBundleIcons,

这让事情变得更加复杂起来(译者:再次叹气~)。这个键下含有子键CFBundlePrimaryIcon,

里面的CFBundleIconFiles子键保存着在此之前保存在Info.plist根节点CFBundleIconFiles键里面的内容。

如果你的应用仅支持iOS 5.0及之后版本,那么用一个 CFBundleIcons键就可以,

否则的话你还需要同时保留CFBundleIconFiles键和相关内容。


(译者:这里原文讲述稍微有点偏差,未提到CFBundlePrimaryIcon,而且比较混乱,令人费解。

其实在Xcode里面以默认方式打开Info.plist看到的会是Icon Files和Icon Files(iOS 5)两组键)

总之,为了做到向后兼容,这个环节会给开发者带来一点小混乱,相当容易犯错。

所以,建议开发者针对不同的设备、屏幕组合多做测试。


三、应用商店截屏图片

苹果近期对应用提交做出了规则调整,如若是iPhone、iPod touch应用,

必须提交Retina显示屏高分版本的应用屏幕截图。

具体图片尺寸要求如下 (前面的尺寸是含系统状态栏情况下的截图):

- 横屏: 960×640 或 960×600

- 竖屏: 640×960 或 640×920


目前苹果尚未对iPad应用提交也做出类似要求,但是为了让你的应用截图在新iPad上看起来呼之欲出,

现在是时候考虑使用高分版本截图了。对应的截图尺寸如下 (前面的尺寸是含有状态栏情况下的截图):

- 横屏: 2048×1536 或 2048×1496像素

- 竖屏: 1536×2048 或 1536×2008像素


译者注:原文评论中有人做了补充的,运行时的载入画面,针对新iPad屏幕也需要准备,文件命名和尺寸要求:

- 横屏: Default-Landscape@2x~ipad.png (2048×1496像素)

- 竖屏: Default-Portrait@2x~ipad.png (1536×2008像素)

问题有所改善但依然有问题:正在处理第 1 篇: https://blog.sina.com.cn/s/blog_475b3d560102w5rf.html ✅ 成功提取 https://blog.sina.com.cn/s/blog_475b3d560102w5rf.html 数据:阅读1679 | 收藏2 | 喜欢17 | 赠金笔0 正在处理第 2 篇: https://blog.sina.com.cn/s/blog_475b3d560102vkkf.html ✅ 成功提取 https://blog.sina.com.cn/s/blog_475b3d560102vkkf.html 数据:阅读0 | 收藏0 | 喜欢472 | 赠金笔268 正在处理第 3: https://blog.sina.com.cn/s/blog_475b3d560102wdtc.html ✅ 成功提取 https://blog.sina.com.cn/s/blog_475b3d560102wdtc.html 数据:阅读0 | 收藏0 | 喜欢1071 | 赠金笔128 正在处理第 4 篇: https://blog.sina.com.cn/s/blog_475b3d560102w5lw.html ❌ 提取 https://blog.sina.com.cn/s/blog_475b3d560102w5lw.html 数据失败: Message: Stacktrace: GetHandleVerifier [0x0x7ff6e669e8e5+80021] GetHandleVerifier [0x0x7ff6e669e9 正在处理第 5 篇: https://blog.sina.com.cn/s/blog_475b3d560102vpz3.html ❌ 提取 https://blog.sina.com.cn/s/blog_475b3d560102vpz3.html 数据失败: Message: Stacktrace: GetHandleVerifier [0x0x7ff6e669e8e5+80021] GetHandleVerifier [0x0x7ff6e669e9 正在处理第 6 篇: https://blog.sina.com.cn/s/blog_475b3d560102wh5w.html ❌ 提取 https://blog.sina.com.cn/s/blog_475b3d560102wh5w.html 数据失败: Message: Stacktrace: GetHandleVerifier [0x0x7ff6e669e8e5+80021] GetHandleVerifier [0x0x7ff6e669e9 正在处理第 7 篇: https://blog.sina.com.cn/s/blog_475b3d560102v8fk.html ❌ 提取 https://blog.sina.com.cn/s/blog_475b3d560102v8fk.html 数据失败: Message: Stacktrace: GetHandleVerifier [0x0x7ff6e669e8e5+80021] GetHandleVerifier [0x0x7ff6e669e9 正在处理第 8 篇: https://blog.sina.com.cn/s/blog_475b3d560102vns9.html
最新发布
10-27
从你给出的部分网页提取失败的堆栈信息 `GetHandleVerifier [0x0x7ff6e669e8e5+80021]` 来看,这通常是和内存管理或者句柄验证有关的错误。要解决从新浪博客网页提取数据部分失败的问题,可从以下几个方面对代码进行修改: 1. **增强异常处理**:在提取数据的函数里,要捕获更多类型的异常,并且详细记录错误信息,这样方便后续排查。 2. **增加重试机制**:对于提取失败的网页,尝试重新提取若干次。 3. **优化等待时间**:动态调整等待时间,保证页面完全加载。 以下是修改后的代码: ```python from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import pandas as pd import requests from bs4 import BeautifulSoup from sqlalchemy import create_engine from urllib.parse import quote_plus import re # 数据库连接配置 DBSERVER = &#39;DESKTOP-NU4IJON&#39; DBNAME = &#39;BlogData&#39; DBUSER = &#39;sa&#39; DBPASSWORD = &#39;1&#39; connection_string = f"DRIVER={{ODBC Driver 17 for SQL Server}};SERVER={DBSERVER};DATABASE={DBNAME};UID={DBUSER};PWD={DBPASSWORD}" connection_url = f"mssql+pyodbc:///?odbc_connect={quote_plus(connection_string)}" try: engine = create_engine(connection_url) print("数据库连接成功!") except Exception as e: print(f"数据库连接失败: {e}") exit() # Chrome浏览器配置(适配141.0.7390.122) def setup_driver(): chrome_options = Options() chrome_options.binary_location = r"D:\Google\chrome-win64\chrome.exe" # 你的Chrome路径 chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--window-size=1920,1080") chrome_options.add_argument( "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.7390.122 Safari/537.36" ) service = Service(r"C:\Users\Lenovo\Downloads\chromedriver-win64\chromedriver.exe") # 你的ChromeDriver路径 driver = webdriver.Chrome(service=service, options=chrome_options) driver.implicitly_wait(10) return driver # 动态数据提取函数(精准匹配元素结构) def get_dynamic_data(driver, url, max_retries=3): retries = 0 while retries < max_retries: try: driver.get(url) time.sleep(10) # 延长等待确保加载 data = {&#39;url&#39;: url, &#39;read_count&#39;: 0, &#39;collect_count&#39;: 0, &#39;like_count&#39;: 0, &#39;gold_pen_count&#39;: 0} # 阅读数:<span id="r_475b3d560102wq5k" class="SG_txtb">(12872)</span> read_elem = WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.XPATH, &#39;//span[contains(@id, "r_") and contains(@class, "SG_txtb")]&#39;)) ) read_text = read_elem.text.strip(&#39;()&#39;) if read_text.isdigit(): data[&#39;read_count&#39;] = int(read_text) # 收藏数:<span id="f_475b3d560102wq5k" class="SG_txtb">(39)</span> collect_elem = WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.XPATH, &#39;//span[contains(@id, "f_") and contains(@class, "SG_txtb")]&#39;)) ) collect_text = collect_elem.text.strip(&#39;()&#39;) if collect_text.isdigit(): data[&#39;collect_count&#39;] = int(collect_text) # 喜欢数:<p ti_title="..." id="dbox2_475b3d560102wq5k" class="count" mnum="206">206</p> like_elem = WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.XPATH, &#39;//p[contains(@class, "count") and @mnum]&#39;)) ) like_text = like_elem.text if like_text.isdigit(): data[&#39;like_count&#39;] = int(like_text) # 赠金笔数:<p class="count" id="goldPan-num">36</p> gold_pen_elem = WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.XPATH, &#39;//p[@id="goldPan-num" and @class="count"]&#39;)) ) gold_pen_text = gold_pen_elem.text if gold_pen_text.isdigit(): data[&#39;gold_pen_count&#39;] = int(gold_pen_text) print(f"✅ 成功提取 {url} 数据:阅读{data[&#39;read_count&#39;]} | 收藏{data[&#39;collect_count&#39;]} | 喜欢{data[&#39;like_count&#39;]} | 赠金笔{data[&#39;gold_pen_count&#39;]}") return data except Exception as e: retries += 1 print(f"❌ 提取 {url} 数据失败(第 {retries} 次尝试): {str(e)[:100]}") time.sleep(5) # 等待一段时间后重试 print(f"❌ 提取 {url} 数据失败,达到最大重试次数") return None # 获取文章链接 def get_all_urls(uid, page): url = f&#39;https://blog.sina.com.cn/s/articlelist_{uid}_0_{page}.html&#39; headers = { &#39;User-Agent&#39;: &#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/141.0.7390.122 Safari/537.36&#39; } try: r = requests.get(url, headers=headers, timeout=15) r.raise_for_status() r.encoding = r.apparent_encoding except requests.RequestException as e: print(f"请求第 {page} 页失败: {e}") return [] soup = BeautifulSoup(r.text, &#39;html.parser&#39;) article_links = [] links = soup.find_all(&#39;a&#39;, href=re.compile(r&#39;/s/blog_475b3d56[\w]+\.html&#39;)) if not links: print(f"第 {page} 页无文章链接") return [] for link in links: full_url = requests.compat.urljoin(url, link[&#39;href&#39;]) article_links.append(full_url) return list(set(article_links)) # 主函数 def main(): uid = &#39;1197161814&#39; TARGET_POST_COUNT = 250 post_count = 0 page = 1 all_dynamic_data = [] try: driver = setup_driver() print("Chrome浏览器启动成功,开始爬取动态数据...\n") except Exception as e: print(f"浏览器启动失败: {e}") return while post_count < TARGET_POST_COUNT: article_urls = get_all_urls(uid, page) if not article_urls: print(f"\n第 {page} 页无更多链接,停止爬取") break for url in article_urls: if post_count >= TARGET_POST_COUNT: break print(f"正在处理第 {post_count + 1} 篇: {url}") dynamic_data = get_dynamic_data(driver, url) if dynamic_data: all_dynamic_data.append(dynamic_data) post_count += 1 time.sleep(3) page += 1 time.sleep(5) driver.quit() print(f"\n--- 爬取结束 ---") valid_data = [d for d in all_dynamic_data if sum(d.values()) > 0] print(f"共处理 {len(all_dynamic_data)} 篇,有效数据 {len(valid_data)} 条") if valid_data: df = pd.DataFrame(valid_data) try: df.to_sql( name=&#39;blog_posts&#39;, con=engine, if_exists=&#39;append&#39;, index=False, chunksize=10 ) print(f"✅ 成功存入 {len(df)} 条数据到数据库!") except Exception as e: print(f"❌ 入库失败: {e}") else: print("❌ 无有效数据可入库") if __name__ == &#39;__main__&#39;: main() ``` ### 代码说明: 1. **异常处理**:在 `get_dynamic_data` 函数中,捕获了所有异常,并且进行重试操作。 2. **重试机制**:`get_dynamic_data` 函数添加了 `max_retries` 参数,默认重试3次。 3. **日志记录**:详细记录每次重试的错误信息,方便后续排查问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值