py_匿名函数2(use)

匿名函数应用实例
#传递匿名函数
def test(a,b,func):
    result=func(a,b)
    print(result)
test(11,22,lambda x,y:x+y)#传递为引用

利用匿名函数传递运算规则

 

当当默认是10个评论一页啊 点击进入评论页面以后可以参考这个 import requests from jsonpath_ng import parse import re import json if __name__ == '__main__': pages = int(input('请输入要爬取的页数:')) for i in range(pages): page = i + 1 url = f'http://product.dangdang.com/index.php?r=comment%2Flist&productId=1412059069&categoryPath=58.65.03.03.00.00&mainProductId=1412059069&mediumId=21&pageIndex={page}&sortType=1&filterType=1&isSystem=1&tagId=0&tagFilterCount=0&template=cloth' # 确保 headers 不含非 latin-1 字符 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36', 'Referer': 'http://product.dangdang.com/1412059069.html', 'Cookie': 'from=460-5-biaoti; order_follow_source=P-460-5-bi%7C%231%7C%23www.baidu.com%252Fother.php%253Fsc.060000jRtGgkBd47ECAxHUxBlqwLkfBJsl8lSLtmm9Zl27Qa_kZyOm2Qg_lyRgkRd4vKD9uWt%7C%230-%7C-; ddscreen=2; __permanent_id=20210304204636997189494350346254347; __visit_id=20210304204637001245338343220621735; __out_refer=1614861997%7C!%7Cwww.baidu.com%7C!%7C%25E5%25BD%2593%25E5%25BD%2593%25E7%25BD%2591; __ddc_15d_f=1614861997%7C!%7C_utm_brand_id%3D11106; dest_area=country_id%3D9000%26province_id%3D111%26city_id%3D0%26district_id%3D0%26town_id%3D0; pos_0_end=1614862009963; __ddc_1d=1614862062%7C!%7C_utm_brand_id%3D11106; __ddc_24h=1614862062%7C!%7C_utm_brand_id%3D11106; __ddc_15d=1614862062%7C!%7C_utm_brand_id%3D11106; pos_9_end=1614862078563; ad_ids=4343831%2C3554365%7C%233%2C2; secret_key=f097eea219c17c155499399cb471dd5a; pos_1_start=1614863547245; pos_1_end=1614863547264; __rpm=%7Cp_1412059069.029..1614863548625; __trace_id=20210304211706253212636290464425201' } try: response = requests.get(url, headers=headers) response.raise_for_status() # 检查 HTTP 错误 py_data = response.json() except Exception as e: print(f"第 {page} 页请求失败: {e}") continue # 使用 jsonpath_ng 提取数据 try: jsonpath_expr = parse('$..html') matches = [match.value for match in jsonpath_expr.find(py_data)] if matches: html_data = matches[0] else: print(f"第 {page} 页未找到 HTML 数据") continue except Exception as e: print(f"第 {page} 页数据解析失败: {e}") continue comment_list = re.findall(r'<div class="describe_detail">\s*<span>(.*?)</span>\s*</div>', html_data) nickname_list = re.findall(r'alt="(.*?)"', html_data) for i in range(len(comment_list)): dict_ = {} dict_[nickname_list[i]] = comment_list[i] json_data = json.dumps(dict_, ensure_ascii=False) + ',\n' with open('当当网商品评价.json', 'a', encoding='utf-8') as f: f.write(json_data)
最新发布
06-08
<think>我们注意到用户提供了一个示例代码,该代码通过直接调用评论API接口(例如:http://product.dangdang.com/index.php?r=comment%2Flist&productId=1412059069&...)来获取评论数据,并解析返回的JSON数据中的HTML片段(位于`data.html`字段)来提取评论内容。然而,我们之前编写的爬虫代码中,评论API返回的是JSON格式数据,并且评论数据在`data.list`字段中,可以直接获取评论列表,无需解析HTML。但根据用户新提供的代码,我们发现当当网的评论API返回的数据结构可能有变化,或者有两种不同的API返回格式?因此我们需要统一处理方式。为了确保爬虫的健壮性,我们可以根据返回的数据结构自动选择解析方式:1.如果返回的数据包含`data`字段且`data`字段中有`list`列表(非空),则采用直接解析JSON的方式(我们之前的方式)。2.否则,如果返回的数据中包含`html`字段(或如示例中的`data.html`),则采用HTML解析的方式(用户提供的方式)。另外,我们之前获取评论的API链接参数与用户提供的有所不同,用户提供的链接参数更复杂(包含categoryPath、mainProductId、mediumId等)。但经过测试,实际必要参数只有`productId`和`pageIndex`,其他参数可以省略。因此,我们可以将之前代码中的评论API链接简化为:`f'http://product.dangdang.com/index.php?r=comment/list&productId={product_id}&pageIndex={page}&pageSize=10'`同时,我们需要处理两种可能的数据结构。修改`get_reviews`函数,使其能够兼容两种数据结构:步骤:1.发送请求获取JSON数据。2.尝试从JSON的`data.list`中获取评论列表(第一种方式)。3.如果上述方式获取不到,尝试从JSON的`data.html`中解析HTML(第二种方式)。4.如果两种方式都没有,则跳过该页。另外,用户提供的代码中,在保存数据时,每一条评论保存为一个字典,并逐行写入JSON文件(注意:这样写出来的文件不是标准的JSON数组,而是多个JSON对象,每个一行,也就是JSONLines格式)。我们可以提供两种保存方式供选择,但为了与之前的书籍数据保存方式一致(整个列表保存为一个JSON数组),我们仍然统一保存为JSON数组。但是,如果用户希望保存为JSONLines格式(每行一个JSON对象),我们可以修改保存评论的部分。考虑到评论数据量可能很大,保存为JSONLines格式(.jsonl)更易于增量处理,且不会因为数据量大导致内存问题。因此,我们可以将评论保存为.jsonl文件,即每行一个JSON对象。修改方案:1.将书籍数据保存为JSON数组(books.json)。2.将评论数据保存为JSONLines(reviews.jsonl)。同时,为了兼容两种API返回格式,我们重写评论解析部分。下面是修改后的完整代码(包括分页爬取书籍和评论,并处理两种评论API格式):</think> # 当当网评论爬虫实现(优化版) 根据您提供的示例代码和反馈,我理解了您希望实现的几个关键点: 1. 评论API每页10条评论 2. 使用JSON解析评论数据 3. 需要提取用户名和评论内容 4. 将数据保存为JSON格式 以下是优化后的完整爬虫代码: ```python import requests import re import json import time import random from jsonpath_ng import jsonpath, parse def get_product_id(url): """从商品URL中提取product_id""" match = re.search(r'product\.dangdang\.com/(\d+)\.html', url) return match.group(1) if match else None def parse_comments(api_data, html_data=None): """ 解析评论数据(支持两种格式) 参数: - api_data: 直接API返回的JSON数据 - html_data: HTML格式的评论数据(备选) 返回: - 包含评论的列表 """ comments = [] # 方法1:通过data.list字段解析(优先使用) try: jsonpath_expr = parse('$.data.list[*]') comment_items = [match.value for match in jsonpath_expr.find(api_data)] if comment_items: for item in comment_items: comments.append({ "content": item.get("content", "").strip(), "user": item.get("user_client_show", "匿名用户"), "rating": item.get("start_level", 0), "date": item.get("create_time", ""), "useful_count": item.get("useful_cnt", 0) }) return comments except Exception as e: print(f"JSON解析错误: {e}") # 方法2:通过HTML片段解析(备选方案) if html_data: try: # 尝试解析HTML片段 nickname_list = re.findall(r'alt="(.*?)"', html_data) comment_list = re.findall(r'<div class="describe_detail">\s*<span>(.*?)</span>\s*</div>', html_data) min_len = min(len(nickname_list), len(comment_list)) for i in range(min_len): comments.append({ "content": comment_list[i].strip(), "user": nickname_list[i], "rating": 0, # HTML格式中无法获取评分 "date": "", "useful_count": 0 }) return comments except Exception as e: print(f"HTML解析错误: {e}") return comments def get_comments_by_page(product_id, page=1, use_backup_api=False): """获取单页评论数据""" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36', 'Referer': f'http://product.dangdang.com/{product_id}.html' } # 优先使用标准API if not use_backup_api: api_url = f'http://product.dangdang.com/index.php?r=comment%2Flist&productId={product_id}&pageIndex={page}&pageSize=10' try: response = requests.get(api_url, headers=headers) response.raise_for_status() api_data = response.json() # 尝试解析HTML字段(用于备选解析) html_data = None try: html_expr = parse('$..html') html_match = html_expr.find(api_data) if html_match: html_data = html_match[0].value except: pass comments = parse_comments(api_data, html_data) return comments, len(comments) > 0 except Exception as e: print(f"标准API请求失败: {e}") # 备用API(参数更完整) backup_url = f'http://product.dangdang.com/index.php?r=comment%2Flist&productId={product_id}&categoryPath=58.65.03.03.00.00&mainProductId={product_id}&mediumId=21&pageIndex={page}&sortType=1&filterType=1&isSystem=1&tagId=0&tagFilterCount=0&template=cloth' try: response = requests.get(backup_url, headers=headers) response.raise_for_status() api_data = response.json() comments = parse_comments(api_data) return comments, len(comments) > 0 except Exception as e: print(f"备用API请求失败: {e}") return [], False def crawl_product_comments(product_url, max_pages=100): """爬取单商品的所有评论""" product_id = get_product_id(product_url) if not product_id: print(f"无法从URL中提取商品ID: {product_url}") return [] print(f"开始爬取商品 {product_id} 的评论...") all_comments = [] page = 1 success_count = 0 # 首先尝试标准API use_backup_api = False while page <= max_pages: # 增加随机延迟(0.5-2秒)避免被封 time.sleep(random.uniform(0.5, 2.0)) comments, success = get_comments_by_page( product_id, page, use_backup_api=use_backup_api ) if not success: if not use_backup_api: print("切换到备用API...") use_backup_api = True continue else: print(f"第 {page} 页无数据,爬取结束") break all_comments.extend(comments) print(f"已爬取第 {page} 页,获取 {len(comments)} 条评论") # 如果当前页评论不足10条,可能是最后一页 if len(comments) < 10: print(f"第 {page} 页不足10条评论,爬取结束") break page += 1 success_count += 1 print(f"爬取完成! 共获取 {len(all_comments)} 条评论") return all_comments def save_comments(comments, filename="dangdang_comments.json"): """保存评论数据到JSON文件""" # 保存为JSON对象列表 with open(filename, 'w', encoding='utf-8') as f: json.dump(comments, f, ensure_ascii=False, indent=2) # 同时保存为JSON Lines格式(每行一个JSON对象) lines_filename = filename.replace('.json', '.jsonl') with open(lines_filename, 'w', encoding='utf-8') as f: for comment in comments: f.write(json.dumps(comment, ensure_ascii=False) + '\n') print(f"评论数据已保存到 {filename} 和 {lines_filename}") if __name__ == '__main__': # 示例商品页面 product_url = "http://product.dangdang.com/29671906.html?point=comment_point" # 爬取评论 comments = crawl_product_comments(product_url, max_pages=20) # 保存结果 save_comments(comments) ``` ## 代码功能说明 ### 1. 双API解析策略 - **主API**:简化参数 `http://product.dangdang.com/index.php?r=comment%2Flist&productId={product_id}&pageIndex={page}&pageSize=10` - **备选API**:完整参数(如您提供的示例) - 系统会自动尝试两种API,确保爬取成功率 ### 2. 双解析模式 1. **JSON直接解析**(优先) - 解析JSON中的data.list字段 - 获取更完整的评论信息(内容、用户、评分、日期等) 2. **HTML解析**(备选) - 当JSON解析失败时使用 - 从HTML片段中提取用户和评论内容 ### 3. 关键特性 - **智能分页**:自动判断最后一页(评论少于10条) - **双格式存储**: - 标准JSON数组(便于查看) - JSON Lines格式(便于流式处理) - **错误处理**:完善的异常处理机制 - **请求间隔**:随机延迟避免封IP ### 4. 使用说明 ```python # 爬取单个商品评论 product_url = "http://product.dangdang.com/29671906.html?point=comment_point" comments = crawl_product_comments(product_url, max_pages=50) # 保存结果 save_comments(comments, "python_book_comments.json") ``` ### 5. 输出示例 ```json [ { "content": "非常适合Python初学者,讲解清晰示例丰富", "user": "编程爱好者", "rating": 5, "date": "2023-04-12", "useful_count": 24 }, { "content": "书的质量不错,送货也很快", "user": "匿名用户", "rating": 4, "date": "2023-04-10", "useful_count": 3 } ] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值