jian_pokemon_card_spider.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. # -*- coding: utf-8 -*-
  2. # Author : Charley
  3. # Python : 3.10.8
  4. # Date : 2025/8/26 10:47
  5. import inspect
  6. import random
  7. import time
  8. import requests
  9. import user_agent
  10. from loguru import logger
  11. from parsel import Selector
  12. from urllib.parse import quote
  13. from tenacity import retry, stop_after_attempt, wait_fixed
  14. from mysql_pool import MySQLConnectionPool
  15. from wx_pokemon_aes_tool import pokemon_aes_encrypt, pokemon_aes_decrypt, api_sign
  16. max_page = 1000
  17. crawler_language = "简中"
  18. logger.remove()
  19. logger.add("./logs/jian_{time:YYYYMMDD}.log", encoding='utf-8', rotation="00:00",
  20. format="[{time:YYYY-MM-DD HH:mm:ss.SSS}] {level} {message}",
  21. level="DEBUG", retention="7 day")
  22. def after_log(retry_state):
  23. """
  24. retry 回调
  25. :param retry_state: RetryCallState 对象
  26. """
  27. # 检查 args 是否存在且不为空
  28. if retry_state.args and len(retry_state.args) > 0:
  29. log = retry_state.args[0] # 获取传入的 logger
  30. else:
  31. log = logger # 使用全局 logger
  32. if retry_state.outcome.failed:
  33. log.warning(
  34. f"Function '{retry_state.fn.__name__}', Attempt {retry_state.attempt_number} Times")
  35. else:
  36. log.info(f"Function '{retry_state.fn.__name__}', Attempt {retry_state.attempt_number} succeeded")
  37. @retry(stop=stop_after_attempt(5), wait=wait_fixed(1), after=after_log)
  38. def get_proxys(log):
  39. """
  40. 获取代理
  41. :return: 代理
  42. """
  43. tunnel = "x371.kdltps.com:15818"
  44. kdl_username = "t13753103189895"
  45. kdl_password = "o0yefv6z"
  46. try:
  47. proxies = {
  48. "http": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": kdl_username, "pwd": kdl_password, "proxy": tunnel},
  49. "https": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": kdl_username, "pwd": kdl_password, "proxy": tunnel}
  50. }
  51. return proxies
  52. except Exception as e:
  53. log.error(f"Error getting proxy: {e}")
  54. raise e
  55. @retry(stop=stop_after_attempt(5), wait=wait_fixed(1), after=after_log)
  56. def get_parent_single_page(log, page, sql_pool):
  57. """
  58. 单页请求
  59. :param log:
  60. :param page:
  61. :param sql_pool:
  62. :return: decrypted_res -> 解密后的数据
  63. """
  64. log.debug(f'Request {inspect.currentframe().f_code.co_name} for page: {page} .................')
  65. par = {"pageNum": str(page), "pageSize": "20"}
  66. sign_result = api_sign(
  67. timeout=1221,
  68. user_token="",
  69. params=par
  70. )
  71. req_data = pokemon_aes_encrypt(sign_result["secretJsonParams"])
  72. headers = {
  73. "Content-Type": "application/json",
  74. "User-Agent": user_agent.generate_user_agent(),
  75. "Api-Access-Token": "",
  76. "Nonce": str(sign_result["nonce"]),
  77. "Signature": sign_result["signature"],
  78. "Timestamp": sign_result["timestamp"],
  79. }
  80. url = "https://app-api.pokemon-tcg.cn/app-api/v1/app/commodity/queryParent"
  81. data = {
  82. "encryptionBodyParams": req_data
  83. }
  84. response = requests.post(url, headers=headers, json=data)
  85. # print(response.text)
  86. response.raise_for_status()
  87. decrypted_res = pokemon_aes_decrypt(response.text)
  88. # print(decrypted_res)
  89. return decrypted_res
  90. def parse_parent_list(log, list_data, sql_pool):
  91. """
  92. 解析 parent 数据
  93. :param log:
  94. :param list_data:
  95. :param sql_pool:
  96. :return:
  97. """
  98. if not list_data:
  99. log.error(f"{inspect.currentframe().f_code.co_name} list_data is None")
  100. return
  101. # info_list = []
  102. for item in list_data:
  103. parent_id = item.get("id")
  104. expansion_img = item.get("imageUrl")
  105. expansion_series = item.get("name")
  106. # data_dict = {
  107. # "parent_id": parent_id,
  108. # "expansion_series": expansion_series,
  109. # "expansion_img": expansion_img,
  110. # # "crawler_language": crawler_language
  111. # }
  112. # print(data_dict)
  113. # info_list.append(data_dict)
  114. try:
  115. log.debug(f"{inspect.currentframe().f_code.co_name} Request -> get_child_list, parent_id: {parent_id}")
  116. get_child_list(log, sql_pool, parent_id, expansion_series, expansion_img)
  117. except Exception as e:
  118. log.error(f"Error parsing child list: {e}")
  119. def get_parent_list(log, sql_pool):
  120. """
  121. 分页获取所有数据,支持多种停止条件
  122. :param log:
  123. :param sql_pool:
  124. """
  125. all_data = []
  126. page = 1
  127. total_fetched = 0 # 已获取的总记录数
  128. total_expected = None # 从第一次响应中获取 total
  129. while page <= max_page:
  130. try:
  131. result = get_parent_single_page(log, page, sql_pool)
  132. if result.get("code") != 0:
  133. log.error(f"请求失败: {result.get('message')}")
  134. break
  135. data = result.get("data", {})
  136. list_data = data.get("list", [])
  137. # 解析数据
  138. parse_parent_list(log, list_data, sql_pool)
  139. total = data.get("total")
  140. pages = data.get("pages")
  141. # has_next_page = data.get("hasNextPage", True) # 默认为 True
  142. # is_last_page = data.get("isLastPage", False) # 接口中该字段有问题
  143. # 记录总条数(首次获取)
  144. if total_expected is None:
  145. total_expected = total
  146. log.info(f"总条数: {total_expected}")
  147. # 停止条件判断
  148. if (
  149. len(list_data) < 20 or # 没有数据
  150. # is_last_page or # API 明确表示是最后一页
  151. (pages and page >= pages) or # 当前页 >= 总页数
  152. (total_expected and total_fetched + len(list_data) >= total_expected) # 已取完所有数据
  153. ):
  154. log.info(f"停止翻页,当前页: {page}, 已获取: {total_fetched + len(list_data)} 条")
  155. break
  156. # 添加到结果
  157. all_data.extend(list_data)
  158. total_fetched += len(list_data)
  159. log.info(f"第 {page} 页获取 {len(list_data)} 条,累计 {total_fetched} 条")
  160. page += 1
  161. except Exception as e:
  162. log.error(f"第 {page} 页请求异常: {e}")
  163. break
  164. log.info(f"共获取 {len(all_data)} 条数据")
  165. # return all_data
  166. # ---------------------------------------------------------------------------------------------------------------------
  167. @retry(stop=stop_after_attempt(5), wait=wait_fixed(1), after=after_log)
  168. def get_child_single_page(log, page, parentId):
  169. """
  170. 获取获取 每个父系列的 子系列 单页数据
  171. :param log:
  172. :param page:
  173. :param parentId: 查询的父系列id
  174. :return:
  175. """
  176. log.debug(f'Request {inspect.currentframe().f_code.co_name} for page: {page} .................')
  177. par = {'pageNum': str(page), 'pageSize': '20', 'parentId': str(parentId)}
  178. sign_result = api_sign(
  179. timeout=1221,
  180. user_token="",
  181. params=par
  182. )
  183. req_data = pokemon_aes_encrypt(sign_result["secretJsonParams"])
  184. headers = {
  185. "Content-Type": "application/json",
  186. "User-Agent": user_agent.generate_user_agent(),
  187. "Api-Access-Token": "",
  188. "Nonce": str(sign_result["nonce"]),
  189. "Signature": sign_result["signature"],
  190. "Timestamp": sign_result["timestamp"],
  191. }
  192. url = "https://app-api.pokemon-tcg.cn/app-api/v1/app/commodity/queryByParentId"
  193. data = {
  194. "encryptionBodyParams": req_data
  195. }
  196. response = requests.post(url, headers=headers, json=data)
  197. # print(response.text)
  198. response.raise_for_status()
  199. decrypted_res = pokemon_aes_decrypt(response.text)
  200. # print(decrypted_res)
  201. return decrypted_res
  202. def parse_child_list(log, list_data, sql_pool, parentId, expansion_series, expansion_img):
  203. """
  204. 解析 child 数据
  205. :param log:
  206. :param list_data:
  207. :param sql_pool:
  208. :param parentId:
  209. :param expansion_series:
  210. :param expansion_img:
  211. :return:
  212. """
  213. if not list_data:
  214. log.error(f"{inspect.currentframe().f_code.co_name} list_data is None")
  215. return
  216. info_list = []
  217. for item in list_data:
  218. child_id = item.get("id")
  219. commodityCode = item.get("commodityCode") # 商品编码
  220. # series = item.get("series")
  221. imageUrl = item.get("imageUrl")
  222. child_name = item.get("name")
  223. salesDate = item.get("salesDate")
  224. description_html = item.get("description")
  225. selector = Selector(description_html)
  226. description_list = selector.xpath('//p/text()').getall()
  227. description = '\n'.join(description_list)
  228. data_dict = {
  229. "parent_id": parentId,
  230. "expansion_series": expansion_series,
  231. "expansion_img": expansion_img,
  232. "child_id": child_id,
  233. "child_name": child_name,
  234. "commodity_code": commodityCode,
  235. # "series": series,
  236. "child_image_url": imageUrl,
  237. "sales_date": salesDate,
  238. "description": description,
  239. "crawler_language": crawler_language
  240. }
  241. # print(data_dict)
  242. info_list.append(data_dict)
  243. if info_list:
  244. sql_pool.insert_many(table="pokemon_jianz_category", data_list=info_list, ignore=True)
  245. def get_child_list(log, sql_pool, parent_id, expansion_series, expansion_img):
  246. """
  247. 获取 每个父系列的 子系列 列表
  248. :param log:
  249. :param sql_pool:
  250. :param parent_id:
  251. :param expansion_series:
  252. :param expansion_img:
  253. :return:
  254. """
  255. all_data = []
  256. page = 1
  257. total_fetched = 0
  258. total_expected = None
  259. while page <= max_page:
  260. try:
  261. result = get_child_single_page(log, page, parent_id)
  262. if result.get("code") != 0:
  263. log.error(f"请求失败: {result.get('message')}")
  264. break
  265. data = result.get("data", {})
  266. list_data = data.get("list", [])
  267. parse_child_list(log, list_data, sql_pool, parent_id, expansion_series, expansion_img)
  268. total = data.get("total")
  269. pages = data.get("pages")
  270. # has_next_page = data.get("hasNextPage", True)
  271. # is_last_page = data.get("isLastPage", False)
  272. # 记录总条数(首次获取)
  273. if total_expected is None:
  274. total_expected = total
  275. log.info(f"总条数: {total_expected}")
  276. if (
  277. len(list_data) < 20 or
  278. # is_last_page or
  279. (pages and page >= pages) or
  280. (total_expected and total_fetched + len(list_data) >= total_expected)
  281. ):
  282. log.info(f"停止翻页,当前页: {page}, 已获取: {total_fetched + len(list_data)} 条")
  283. break
  284. all_data.extend(list_data)
  285. total_fetched += len(list_data)
  286. log.info(f"第 {page} 页获取 {len(list_data)} 条,累计 {total_fetched} 条")
  287. page += 1
  288. except Exception as e:
  289. log.error(f"第 {page} 页请求异常: {e}")
  290. break
  291. log.info(f"共获取 {len(all_data)} 条数据")
  292. # ---------------------------------------------------------------------------------------------------------------------
  293. @retry(stop=stop_after_attempt(5), wait=wait_fixed(1), after=after_log)
  294. def get_series_single_page(log, item_tuple, page):
  295. """
  296. 获取 每个子系列的 列表页 单页请求
  297. :param log:
  298. :param item_tuple:
  299. :param page:
  300. :return:
  301. """
  302. child_id = item_tuple[0]
  303. child_name = item_tuple[1]
  304. commodity_code = item_tuple[2]
  305. sales_date = item_tuple[3]
  306. # print(child_id, child_name, commodity_code, sales_date)
  307. log.debug(f'Request {inspect.currentframe().f_code.co_name} for ID: {child_id}, page: {page} .................')
  308. par = {"banCardFlag": "0", "commodityIds": str(child_id), "commoditySelectedList": [
  309. {"id": str(child_id), "commodityName": child_name, "commodityCode": commodity_code, "salesDate": sales_date}],
  310. "pageNum": str(page), "pageSize": "50"}
  311. # print( par)
  312. # test_string = '{"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb'
  313. # 字符串:{"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb
  314. sign_result = api_sign(
  315. timeout=1221,
  316. user_token="",
  317. params=par,
  318. need_md5=True
  319. )
  320. # print(sign_result)
  321. # print(type(sign_result))
  322. # req_data = pokemon_aes_encrypt(sign_result["secretJsonParams"])
  323. req_data = pokemon_aes_encrypt(par)
  324. # print(req_data)
  325. headers = {
  326. "Content-Type": "application/json",
  327. "User-Agent": user_agent.generate_user_agent(),
  328. "Api-Access-Token": "",
  329. "Nonce": str(sign_result["nonce"]),
  330. "Signature": sign_result["signature"],
  331. "Timestamp": sign_result["timestamp"],
  332. }
  333. url = "https://app-api.pokemon-tcg.cn/app-api/v1/app/card/query"
  334. data = {
  335. "encryptionBodyParams": req_data
  336. }
  337. response = requests.post(url, headers=headers, json=data)
  338. # print(response.text)
  339. response.raise_for_status()
  340. decrypted_res = pokemon_aes_decrypt(response.text)
  341. # print(decrypted_res)
  342. return decrypted_res
  343. def parse_series(log, list_data, sql_pool, item_tuple):
  344. """
  345. 解析 子系列 详情 数据
  346. :param log:
  347. :param list_data:
  348. :param sql_pool:
  349. :param item_tuple:
  350. :return:
  351. """
  352. if not list_data:
  353. log.error(f"{inspect.currentframe().f_code.co_name} list_data is None")
  354. return
  355. # info_list = []
  356. for item in list_data:
  357. detail_id = item.get("id") # int
  358. # yoren_code = item.get("yorenCode") # 洋文代码
  359. # cdnImgUrl = item.get("cdnImgUrl")
  360. # cardType = item.get("cardType")
  361. # nameSamePokemonId = item.get("nameSamePokemonId") # 口袋妖怪id int
  362. # data_dict = {
  363. # "child_id": child_id,
  364. # "card_id": detail_id,
  365. # "crawler_language": crawler_language
  366. #
  367. # }
  368. # print(data_dict)
  369. # info_list.append(data_dict)
  370. try:
  371. get_details_page(log, detail_id, item_tuple, sql_pool)
  372. except Exception as e:
  373. log.error(f"{inspect.currentframe().f_code.co_name} {detail_id} 请求异常: {e}")
  374. time.sleep(random.randint(1, 5) / 10)
  375. # if info_list:
  376. # sql_pool.insert_many(table="pokemon_card_child", data_list=info_list, ignore=True)
  377. def get_series_list(log, item_tuple, sql_pool):
  378. """
  379. 获取 每个子系列的 列表页
  380. :param log:
  381. :param item_tuple:
  382. :param sql_pool:
  383. :return:
  384. """
  385. all_data = []
  386. page = 1
  387. total_fetched = 0
  388. total_expected = None
  389. while page <= max_page:
  390. try:
  391. result = get_series_single_page(log, item_tuple, page)
  392. if result.get("code") != 0:
  393. log.error(f"请求失败: {result.get('message')}")
  394. break
  395. data = result.get("data", {})
  396. list_data = data.get("list", [])
  397. parse_series(log, list_data, sql_pool, item_tuple)
  398. total = data.get("total")
  399. pages = data.get("pages")
  400. # 记录总条数(首次获取)
  401. if total_expected is None:
  402. total_expected = total
  403. log.info(f"总条数: {total_expected}")
  404. # 停止条件判断
  405. if (
  406. len(list_data) < 20 or # 没有数据
  407. (pages and page >= pages) or # 当前页 >= 总页数
  408. (total_expected and total_fetched + len(list_data) >= total_expected) # 已取完所有数据
  409. ):
  410. log.info(f"停止翻页,当前页: {page}, 已获取: {total_fetched + len(list_data)} 条")
  411. break
  412. # 添加到结果
  413. all_data.extend(list_data)
  414. total_fetched += len(list_data)
  415. log.info(f"第 {page} 页获取 {len(list_data)} 条,累计 {total_fetched} 条")
  416. page += 1
  417. except Exception as e:
  418. log.error(f"第 {page} 页请求异常: {e}")
  419. break
  420. # ---------------------------------------------------------------------------------------------------------------------
  421. @retry(stop=stop_after_attempt(5), wait=wait_fixed(1), after=after_log)
  422. def get_details_page(log, card_id, item_tuple, sql_pool):
  423. """
  424. 获取 详情页 数据
  425. :param log:
  426. :param card_id:
  427. :param item_tuple:
  428. :param sql_pool:
  429. """
  430. log.debug(
  431. f'Request {inspect.currentframe().f_code.co_name} for card_id: {card_id} .................')
  432. par = {"id": str(card_id)}
  433. sign_result = api_sign(
  434. timeout=1221,
  435. user_token="",
  436. params=par,
  437. # need_md5=True
  438. )
  439. # print(f"解密结果: {sign_result}")
  440. # print(type(sign_result))
  441. # req_data = pokemon_aes_encrypt(sign_result["secretJsonParams"])
  442. req_data = pokemon_aes_encrypt(par)
  443. # E = this.encryptionEnable ? "{}" == A ? {} : n({}, "GET" == o ? "encryptionUrlParams" : "encryptionBodyParams", "GET" == o ? encodeURIComponent(P) : P) : i,
  444. # Python 的 quote() 函数默认会将空格编码为 + 为了模拟 encodeURIComponent() 将空格编码为 %20 的行为,必须将 safe 参数设置为空字符串 ''
  445. req_data = quote(req_data, safe='')
  446. # print(req_data)
  447. headers = {
  448. "Content-Type": "application/json",
  449. "User-Agent": user_agent.generate_user_agent(),
  450. "Api-Access-Token": "",
  451. "Nonce": str(sign_result["nonce"]),
  452. "Signature": sign_result["signature"],
  453. "Timestamp": sign_result["timestamp"]
  454. }
  455. url = "https://app-api.pokemon-tcg.cn/app-api/v1/app/card/get"
  456. params = {
  457. # "encryptionUrlParams": "%2BsGoBpMkJzVjwAgE2Gca2Q%3D%3D"
  458. "encryptionUrlParams": req_data
  459. }
  460. response = requests.get(url, headers=headers, params=params)
  461. # print('response.text:', response.text)
  462. response.raise_for_status()
  463. decrypted_res = pokemon_aes_decrypt(response.text)
  464. # print('decrypted_res:', decrypted_res)
  465. try:
  466. parse_details(log, decrypted_res, sql_pool, card_id, item_tuple)
  467. except Exception as e:
  468. log.error(f"解析详情页数据异常: {e}")
  469. def parse_details(log, list_data, sql_pool, card_id, item_tuple):
  470. """
  471. 解析详情页数据
  472. :param log:
  473. :param list_data:
  474. :param sql_pool:
  475. :param card_id:
  476. :param item_tuple:
  477. :return:
  478. """
  479. child_id = item_tuple[0]
  480. child_name = item_tuple[1]
  481. commodity_code = item_tuple[2]
  482. expansion_series = item_tuple[4]
  483. """
  484. pg_value -> commodity_code
  485. pg_label -> child_name
  486. major_category_name -> expansion_series
  487. """
  488. if not list_data:
  489. log.error(f"{inspect.currentframe().f_code.co_name} list_data is None")
  490. return
  491. item = list_data.get("data")
  492. if not item:
  493. log.error(f"{inspect.currentframe().f_code.co_name} item is None")
  494. return
  495. # pg_value
  496. # major_category_name
  497. img = item.get("imgUrl")
  498. # evolveText = item.get("evolveText")
  499. card_name = item.get("cardName")
  500. # regulationMarkText = item.get("regulationMarkText")
  501. card_no = item.get("collectionNumber")
  502. rarity = item.get("rarityText")
  503. data_dict = {
  504. "child_id": child_id,
  505. "major_category_name": expansion_series,
  506. "pg_value": commodity_code,
  507. "pg_label": child_name,
  508. "card_id": card_id,
  509. "card_name": card_name,
  510. "card_no": card_no,
  511. "rarity": rarity,
  512. # "regulationMarkText": regulationMarkText,
  513. "img": img,
  514. "crawler_language": crawler_language
  515. }
  516. # print(data_dict)
  517. sql_pool.insert_one_or_dict(table="pokemon_card_record", data=data_dict, ignore=True)
  518. @retry(stop=stop_after_attempt(100), wait=wait_fixed(3600), after=after_log)
  519. def jz_pokemon_main(log):
  520. """
  521. 主函数
  522. """
  523. log.info(f'开始运行 {inspect.currentframe().f_code.co_name} 爬虫任务.............................................')
  524. # 配置 MySQL 连接池
  525. sql_pool = MySQLConnectionPool(log=log)
  526. if not sql_pool.check_pool_health():
  527. log.error("数据库连接池异常")
  528. raise RuntimeError("数据库连接池异常")
  529. try:
  530. # 获取分类列表
  531. # log.debug(".......... 获取分类列表 ..........")
  532. # try:
  533. # get_parent_list(log, sql_pool)
  534. # except Exception as e:
  535. # log.error(f"{inspect.currentframe().f_code.co_name} Request get_category_list error: {e}")
  536. # 获取商品详情
  537. log.debug(f"........... 获取商品详情 ..........")
  538. # par = {"banCardFlag": "0", "commodityIds": "279", "commoditySelectedList": [
  539. # {"id": "279", "commodityName": "收集啦151 惊", "commodityCode": "151C3", "salesDate": "2025-07-18"}],
  540. # "pageNum": str(page), "pageSize": "50"}
  541. sql_ietm_id_list = sql_pool.select_all(
  542. f"SELECT DISTINCT child_id,child_name,commodity_code,sales_date,expansion_series FROM pokemon_jianz_category WHERE crawler_language='{crawler_language}'")
  543. # sql_ietm_id_list = [item_id[0] for item_id in sql_ietm_id_list]
  544. log.debug(f"获取商品详情长度为: {len(sql_ietm_id_list)}")
  545. for item_tuple in sql_ietm_id_list:
  546. try:
  547. get_series_list(log, item_tuple, sql_pool)
  548. # get_details_page(log, item_id, sql_pool)
  549. except Exception as e:
  550. log.error(f"Request get_details error: {e}")
  551. except Exception as e:
  552. log.error(f'{inspect.currentframe().f_code.co_name} error: {e}')
  553. finally:
  554. log.info(f'爬虫程序 {inspect.currentframe().f_code.co_name} 运行结束,等待下一轮的采集任务............')
  555. if __name__ == '__main__':
  556. # get_parent_list(logger, None)
  557. # get_child_list(logger, None)
  558. # get_series_list(logger, None)
  559. # get_details_page(logger, 11364, None)
  560. jz_pokemon_main(logger)