会员购项目面试题解析:高效数据抓取与异常处理

打印 上一主题 下一主题

主题 1693|帖子 1693|积分 5079

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
会员购项目

亮点



  • 日志记载信息
  • 协程异步抓取数据,大大提高抓取速度
  • 捕获异常,并添加重试机制
源码

  1. import logging
  2. import time
  3. import requests
  4. import asyncio
  5. import aiohttp
  6. from aiohttp import ContentTypeError
  7. import csv
  8. # 配置日志
  9. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s : %(message)s')
  10. # 解析数据
  11. def parse_data(data):
  12.     if data:
  13.         for meeting in data:
  14.             project_id = meeting['project_id']
  15.             project_name = meeting['project_name']
  16.             start_time = meeting['start_time']
  17.             venue_name = meeting['venue_name']
  18.             price_low = meeting['price_low'] / 100
  19.             price_high = meeting['price_high'] / 100
  20.             yield {
  21.                 'project_id': project_id,
  22.                 'project_name': project_name,
  23.                 'start_time': start_time,
  24.                 'venue_name': venue_name,
  25.                 'price_low': price_low,
  26.                 'price_high': price_high
  27.             }
  28. # 保存至csv文件中
  29. def save_file(city_info, city_id):
  30.     if city_info:
  31.         with open(f'{city_id}.csv', 'a+', newline='', encoding='utf-8') as f:
  32.             writer = csv.writer(f)
  33.             writer.writerow([f'{city_info["project_id"]}', f'{city_info["project_name"]}', f'{city_info["start_time"]}',
  34.                              f'{city_info["venue_name"]}', f'{city_info["price_low"]}', f'{city_info["price_high"]}'])
  35. class Myspider(object):
  36.     types_list = ['演出', '展览', '本地生活']
  37.     cities_id_list = []
  38.     failed_urls = []
  39.     CONCURRENTCY = 4
  40.     RETRY_LIMIT = 3
  41.     def __init__(self):
  42.         self.session = None
  43.         self.semaphore = asyncio.Semaphore(Myspider.CONCURRENTCY)
  44.     # 获取城市编号并设置类属性
  45.     @staticmethod
  46.     def set_cities_id():
  47.         headers = {
  48.             'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0'}
  49.         cities_data = requests.get("https://show.bilibili.com/api/ticket/city/list?channel=4", headers=headers).json()[
  50.             'data']
  51.         developed_cities_id = [city['id'] for city in cities_data['list']]
  52.         developing_cities_id = [city['id'] for part in cities_data['more'] for city in part['list']]
  53.         Myspider.cities_id_list = developed_cities_id + developing_cities_id
  54.         return None
  55.     # 解决单个任务,爬取相关信息
  56.     async def get_every_page_info(self, url):
  57.         async with self.semaphore:
  58.             logging.info(f"scraping {url}")
  59.             for attempt in range(Myspider.RETRY_LIMIT):
  60.                 try:
  61.                     async with self.session.get(url) as response:
  62.                         data = await response.json()
  63.                         return data["data"]["result"]
  64.                 except ContentTypeError:
  65.                     logging.info(f"error ocurred when scraping {url}", exc_info=True)
  66.                 except aiohttp.ClientError as e:
  67.                     logging.error(f"ClientError on {url}: {e}", exc_info=True)
  68.                     if attempt < Myspider.RETRY_LIMIT - 1:
  69.                         await asyncio.sleep(2 ** attempt)  # Exponential backoff
  70.                         continue
  71.                 except aiohttp.ServerDisconnectedError:
  72.                     logging.error(f"Server disconnected: {url}", exc_info=True)
  73.                     if attempt < Myspider.RETRY_LIMIT - 1:
  74.                         await asyncio.sleep(2 ** attempt)
  75.                         continue
  76.             Myspider.failed_urls.append(url)
  77.             return None  # Return None if all retry attempts fail
  78.     # 获取 此分类下 此城市下 最大页数
  79.     def get_max_page(self, url):
  80.         headers = {
  81.             'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0'}
  82.         response = requests.get(url, headers=headers)
  83.         data = response.json()
  84.         return data["data"]["numPages"]
  85.     # 主方法, 获取任务列表, 开4个协程去抓
  86.     async def main(self):
  87.         headers = {
  88.             'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0'}
  89.         # 初始化session(主要加header头信息以及代理,cookie等头信息)
  90.         async with aiohttp.ClientSession(headers=headers) as session:
  91.             self.session = session
  92.             for type in Myspider.types_list:
  93.                 for city_id in Myspider.cities_id_list:
  94.                     begin_url = "https://show.bilibili.com/api/ticket/project/listV2?version=134&page=1&pagesize=16&area={}&filter=&platform=web&p_type={}".format(
  95.                         city_id, type)
  96.                     max_page = self.get_max_page(begin_url)
  97.                     # 生成任务列表
  98.                     scrapy_tasks = [self.get_every_page_info(
  99.                         "https://show.bilibili.com/api/ticket/project/listV2?version=134&page={}&pagesize=16&area={}&filter=&platform=web&p_type={}".format(
  100.                             page, city_id, type)) for page in range(1, max_page + 1)]
  101.                     # 并发执行任务,获取执行结果
  102.                     scrapy_results = await asyncio.gather(*scrapy_tasks)
  103.                     # 解析结果数据
  104.                     for result in scrapy_results:
  105.                         data = parse_data(result)
  106.                         for city_info in data:
  107.                             print(city_info)
  108.                             save_file(city_info, city_id)
  109.             # 关闭连接
  110.             await self.session.close()
  111. if __name__ == '__main__':
  112.     # 开始时间
  113.     start_time = time.time()
  114.     # 获取城市编号,设置类属性cities_id_list
  115.     Myspider.set_cities_id()
  116.     # 初始化Myspider
  117.     spider = Myspider()
  118.     # 创建事件循环池
  119.     loop = asyncio.get_event_loop()
  120.     # 注册
  121.     loop.run_until_complete(spider.main())
  122.     # 结束事件
  123.     end_time = time.time()
  124.     logging.info(f"total_time: {end_time - start_time}")
  125.     # print(spider.get_max_page('https://show.bilibili.com/api/ticket/project/listV2?version=134&page=1&pagesize=16&area=110100&filter=&platform=web&p_type=%E5%85%A8%E9%83%A8%E7%B1%BB%E5%9E%8B'))
复制代码
更多精致内容: [CodeRealm]

[外链图片转存失败,源站大概有防盗链机制,建议将图片保存下来直接上传(img-BB6kKZj6-1722175080359)(https://i-blog.csdnimg.cn/direct/e18ac94120d945d28ffc46243559ba96.png#pic_center)]

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美丽的神话

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表