资讯专栏INFORMATION COLUMN

Python爬虫实战: 通用版豆瓣电影数据及图片的获取与入库,含防呆逻辑

ckllj / 987人阅读

摘要:电影讲述由浩克斯扮演的酗酒前警察偶然发现一具女尸,并不慎将他的家庭至于危险之中,他不得不一边寻找凶手,一边与恶势力作斗争。该片由内尔姆斯兄弟执导,目前正在拍摄中。

由于最近需要准备一些数据,故开始练习使用胶水语言,经过一番探索终于完成了豆瓣电影信息的爬取,特此分享.

需要说明的是,我这里把电影信息提取之后,缓存了电影封面和演职人员的图片,并对图片信息进行了获取入库

先贴出我两种表结构:

</>复制代码

  1. 1.电影表:

</>复制代码

  1. 其中data是存储电影信息的json数据,如下:
  2. {"mActorRole": [{"name": "奥克塔维亚·斯宾瑟 ", "id": 1154263, "role": "暂无角色信息"},{"name": "约翰·浩克斯 ", "id": 1100849, "role": "暂无角色信息"},{"name": "凯蒂·洛茨 ", "id": 1232918, "role": "暂无角色信息"},{"name": "詹姆斯·拉夫尔提 ", "id": 1022640, "role": "暂无角色信息"},{"name": "小克利夫顿·克林斯 ", "id": 1019033, "role": "暂无角色信息"}], "mCoverId": 2499355591, "mDirector": [{"name": "Eshom Nelms ", "id": 1387484}], "mId": 26802500, "mLength": "92分钟", "mName": "小城犯罪 Small Town Crime", "mShowDate": "2017-03-11(美国)", "mSynopsis": "   奥克塔维亚·斯宾瑟将携约翰·浩克斯出演独立惊悚片《小城犯罪》。电影讲述由浩克斯扮演的酗酒前警察偶然发现一具女尸,并不慎将他的家庭至于危险之中,他不得不一边寻找凶手,一边与恶势力作斗争。该片由内尔姆斯兄弟执导,目前正在拍摄中。 ", "mType": "惊悚"}
  3. 2.图片表:


两个表,结构一致名字不同,id为电影表内演职人员的id.data为图片属性的json字符串,如下:
{"format": "jpg", "height": 383, "size": 20016, "width": 270}

脚本由以下方法调用执行:
需要注意的是 , 下图中的local_url是 豆瓣最新电影 这个页面缓存到本地后的html文件的路径 , 也可以不传 .

</>复制代码

  1. # coding=utf-8
  2. import os
  3. import sys
  4. import cv2
  5. import pymysql
  6. import re
  7. import json
  8. import time
  9. import requests
  10. from bs4 import BeautifulSoup
  11. def get_all_url(local_url, extra_url_list):
  12. """
  13. :param local_url: 电影列表采取动态解析 , 故需要下载解析完毕的数据 . 可为空
  14. :param extra_url_list: 有时候新出的电影凑不够一页,所以手动加在数组里传进来
  15. :return: 单条电影url的list
  16. """
  17. if local_url is not None:
  18. html_file = open(local_url, "r", encoding="UTF-8") # NOTE 只读打开 注意编码
  19. try:
  20. html_page = html_file.read()
  21. except IOError:
  22. print("--------------------本地html读取失败! ------------------------")
  23. return
  24. soup = BeautifulSoup(html_page, "html.parser")
  25. new_list = soup.findAll("a", {"class": "item"})
  26. print("本次预计进行更新的电影数据个数为: %d" % (len(new_list) + len(extra_url_list)))
  27. for movie in new_list:
  28. movie_url = movie.get("href")
  29. extra_url_list.append(movie_url)
  30. if len(extra_url_list) == 0:
  31. print("没有收到任何url , 请检查参数 !")
  32. sys.exit(0)
  33. command = int(input("本次即将进行更新的电影数据个数为 : %d 个 , 按<1>确认更新,按<0>取消更新.....
  34. " % len(extra_url_list)))
  35. i = 0
  36. while i < 2:
  37. if command == 1:
  38. return extra_url_list
  39. if command == 0:
  40. print("本次更新已取消 ! ")
  41. sys.exit(0)
  42. else:
  43. i += 1
  44. command = int(input("您的输入有误 , 还有%d次机会 ! 请再次输入..." % (3 - i)))
  45. print("检查一下你的输入法 ! 再见 ! ")
  46. sys.exit(0)
  47. def frisk_image(folder, table):
  48. if not os.path.exists(folder):
  49. print("-------------------------- %s 不存在 , 请检查 !-------------------------------" % folder)
  50. return
  51. start_time = time.time()
  52. m_image = pymysql.Connect(
  53. host="数据库主机", port=3306,
  54. user="用户名", passwd="密码",
  55. db="图片表名", charset="utf8") # NOTE 一定要设置编码
  56. image_cursor = m_image.cursor()
  57. count = 0
  58. for parent, dir_names, file_names in os.walk(folder):
  59. for file_name in file_names:
  60. full_name = os.path.join(parent, file_name) # note 全路径名
  61. try:
  62. img = cv2.imread(full_name)
  63. except Exception as err:
  64. print("-------------------------------读取<%s>时发生错误 !------------------------------ " % full_name)
  65. print(err)
  66. continue
  67. shape = img.shape
  68. image_data = {"format": re.findall(r"[^.]+$", file_name)[0], "height": int(shape[0]),
  69. "size": int(os.path.getsize(full_name)), "width": int(shape[1])}
  70. img_id = int(
  71. file_name.replace(re.findall(r"[^.]+$", file_name)[0], "").replace(".", "").replace("p",
  72. ""))
  73. json_data = json.dumps(image_data,
  74. sort_keys=True, ensure_ascii=False).replace(""", """) # NOTE 格式化
  75. image_cursor.execute("select count(*) from %s where id =%d" % (table, img_id)) # note 有记录则跳过
  76. if not image_cursor.fetchall()[0][0] == 0:
  77. print("----------------------图片库中已存在id为<%d>的数据 ! 已跳过---------------------" % img_id)
  78. continue
  79. sql = "INSERT INTO %s (id,data, create_time) VALUES (%d , "%s", %d)" % (
  80. table, img_id, json_data, round(time.time() * 1000))
  81. try:
  82. image_cursor.execute(sql)
  83. m_image.commit()
  84. except Exception as err:
  85. print(err)
  86. print("-------------------<%s>在写入数据库过程中出现问题 , 已回滚 !---------------------" % full_name)
  87. m_image.rollback() # NOTE 出错则回滚
  88. continue
  89. print("恭喜! %s 已成功入库啦!" % file_name)
  90. count += 1
  91. m_image.close() # TODO 别忘了这个!
  92. print("恭喜您入库完成 ! 本次入库%d条数据,用时%d秒" % (count, time.time() - start_time))
  93. def get_movie_info(info_table, cover_table, avatar_table,
  94. cover_folder, avatar_folder,
  95. extra_url_list, local_url=None):
  96. start_time = time.time()
  97. url_list = get_all_url(local_url, extra_url_list)
  98. mask_tmp = pymysql.Connect(
  99. host="数据库主机", port=3306,
  100. user="用户名", passwd="密码",
  101. db="电影表名", charset="utf8") # NOTE 一定要设置编码
  102. movie_cursor = mask_tmp.cursor()
  103. headers = {
  104. "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36"
  105. }
  106. count = 0
  107. for url in url_list:
  108. print(">>>>>>>>>>>>>>>>>>>>>>第" + str(count + 1) + "个开始<<<<<<<<<<<<<<<<<<<<<<<<<")
  109. data = requests.get(url, headers=headers).content
  110. row_id = 0
  111. soup = BeautifulSoup(data, "html.parser") # NOTE 实例化一个BeautifulSoup对象
  112. movie_data = {}
  113. try:
  114. m_id = re.findall(r"ect/(.+?)/", url)[0]
  115. if m_id.isdigit(): # NOTE 校验一下mId是否为数字
  116. movie_data["mId"] = int(m_id) # NOTE 设置id为原站id
  117. else:
  118. continue
  119. except Exception as err:
  120. print(err)
  121. print( str(url))+">>>>>>>>>>>>>>>>>>>>>>>>>>>>>解析id出错 !"
  122. continue
  123. has_movie = movie_cursor.execute(
  124. "select id from " + str(info_table) + " where data like "%mId":" + str(m_id) + ",%"")
  125. if not has_movie == 0:
  126. row_id = int(movie_cursor.fetchall()[0][0])
  127. print("发现重复电影>>>>>>>>>>>>>>>>>>mId =<%d> row_id=<%d> , ! " % (int(m_id), row_id))
  128. try:
  129. if soup.find("span", {"property": "v:itemreviewed"}) is None: # NOTE 这个和下面的一样,如果标签找不到就跳过此项数据(或将此数据置默认值)
  130. continue
  131. else:
  132. movie_data["mName"] = soup.find("span", {"property": "v:itemreviewed"}).getText() # NOTE 设置电影名
  133. if soup.find("span", {"property": "v:summary"}) is not None:
  134. movie_data["mSynopsis"] = soup.find("span", {"property": "v:summary"}).getText().replace("
  135. ",
  136. "").replace("
  137. ",
  138. "").replace(
  139. "v", "").replace("
  140. ", "") # NOTE 设置简介( 去掉格式符号)
  141. else:
  142. movie_data["mSynopsis"] = "暂无电影简介"
  143. if len(soup.findAll("li", {"class": "celebrity"})) == 0:
  144. continue
  145. else:
  146. director__actor = soup.findAll("li", {"class": "celebrity"}) # NOTE 演职人员数据都在一起
  147. directors = []
  148. for single_director in director__actor:
  149. if single_director.find("span", {"title": "导演"}) is not None:
  150. director = {}
  151. if single_director.find("a", {"class": "name"}) is not None:
  152. a = single_director.find("a", {"class": "name"})
  153. director["name"] = a.getText()
  154. director["id"] = int(re.findall(r"/celebrity/(.+?)/", a["href"])[0])
  155. inner_all_url = re.findall(r"url((.+?))",
  156. single_director.find("div", {"class": "avatar"})["style"])
  157. img_url = inner_all_url[len(inner_all_url) - 1] # NOTE 图片url (部分人的图片不止一张,最后一张一定人头像)
  158. if img_url.find("default") == -1: # NOTE 初步发现默认图的路径包含 default 如果没有应该就是有图的
  159. if not os.path.exists(avatar_folder):
  160. os.makedirs(avatar_folder)
  161. try:
  162. director_pic = requests.get(img_url, timeout=15)
  163. file_name = "%s/%s" % (avatar_folder,
  164. str(director["id"]) + "." + re.findall(r"[^.]+$", img_url)[
  165. 0]) # NOTE 数值id拼接格式,格式为最后一个.后的字符串 图片id为导演id
  166. fp = open(file_name, "wb")
  167. fp.write(director_pic.content)
  168. fp.close()
  169. print("导演图片 : %s 成功保存到本地 ! " % file_name)
  170. except Exception as err:
  171. print(err)
  172. print(" %s>>>>>>>>>>>>>>>>>在处理导演图片时出错!" % url)
  173. directors.append(director)
  174. movie_data["mDirector"] = [",".join(str(i) for i in directors)] # NOTE 导演
  175. actors = []
  176. for single_actor in director__actor:
  177. has_role = True
  178. li = single_actor.find("span", {"class", "role"})
  179. if li is None: # NOTE 没有角色
  180. has_role = False
  181. else:
  182. if li.getText().find("饰") == -1: # NOTE title与文本不同 肯定不是演员
  183. continue
  184. act = {}
  185. a = single_actor.find("a", {"class": "name"})
  186. if a is None:
  187. continue
  188. act["name"] = a.getText()
  189. act["id"] = int(re.findall(r"/celebrity/(.+?)/", a["href"])[0])
  190. act["role"] = single_actor.find("span", {"class", "role"}).getText().replace("饰 ",
  191. "") if has_role else "暂无角色信息"
  192. inner_all_url = re.findall(r"url((.+?))",
  193. single_actor.find("div", {"class": "avatar"})["style"])
  194. img_url = inner_all_url[len(inner_all_url) - 1] # NOTE 图片url (部分人的图片不止一张,最后一张一定人头像)
  195. if img_url.find("default") == -1: # NOTE 初步发现默认图的路径包含 default 如果没有应该就是有图的
  196. if not os.path.exists(avatar_folder):
  197. os.makedirs(avatar_folder)
  198. try:
  199. actor_pic = requests.get(img_url, timeout=15)
  200. file_name = "%s/%s" % (avatar_folder,
  201. str(act["id"]) + "." + re.findall(r"[^.]+$", img_url)[
  202. 0]) # NOTE 数值id拼接格式,格式为最后一个.后的字符串 图片id 为演员id
  203. fp = open(file_name, "wb")
  204. fp.write(actor_pic.content)
  205. fp.close()
  206. print("演员图片 : %s 成功保存到本地 ! " % file_name)
  207. except Exception as err:
  208. print(err)
  209. print("<%s>>>>>>>>>>>>>>>>>>>>> 在处理演员图片时出错!" % url)
  210. actors.append(act)
  211. movie_data["mActorRole"] = [",".join(str(i) for i in actors)] # NOTE 演员
  212. if len(soup.findAll("span", {"property": "v:genre"})) == 0:
  213. continue
  214. else:
  215. m_type = soup.findAll("span", {"property": "v:genre"})
  216. types = []
  217. for single_type in m_type:
  218. types.append(single_type.getText())
  219. movie_data["mType"] = " / ".join(str(i) for i in types) # NOTE 设置类型 , 结果格式为 : 爱情 / 动作 / 伦理
  220. if len(soup.findAll("span", {"property": "v:initialReleaseDate"})) == 0:
  221. continue
  222. else:
  223. show_date_list = soup.findAll("span", {"property": "v:initialReleaseDate"})
  224. show_date = []
  225. for single_date in show_date_list:
  226. show_date.append(single_date.getText())
  227. movie_data["mShowDate"] = " / ".join(str(i) for i in show_date)
  228. movie_data["mLength"] = soup.find("span", {"property": "v:runtime"}).getText()
  229. if soup.find("span", {"property": "v:runtime"}) is not None else "暂无时长" # NOTE 电影时长
  230. img_url = soup.find("img", {"title": "点击看更多海报"})["src"]
  231. if img_url.find("default") == -1: # NOTE 初步发现默认图的路径包含 default 如果没有应该就是有图的
  232. img_name = re.findall(r"lic/.*", img_url)[0].replace("lic/", "")
  233. if not os.path.exists(cover_folder):
  234. os.makedirs(cover_folder)
  235. try:
  236. cover = requests.get(img_url, timeout=15)
  237. file_name = "%s/%s" % (cover_folder, img_name)
  238. fp = open(file_name, "wb")
  239. fp.write(cover.content)
  240. fp.close()
  241. print("封面图片 : %s 成功保存到本地 ! " % file_name)
  242. except Exception as err:
  243. print(err)
  244. print("url为----> %s 的电影在存储封面时出错!" % url)
  245. movie_data["mCoverId"] = 0
  246. movie_data["mCoverId"] = int(re.findall(r"ic/p(.+?).", img_name)[0])
  247. else:
  248. movie_data["mCoverId"] = 0 # NOTE 没封面的电影图片指向0
  249. json_data = json.dumps(movie_data, sort_keys=True, ensure_ascii=False).replace(""", """).replace("["",
  250. "[").replace(
  251. ""]", "]") # NOTE 格式化输出电影信息
  252. except Exception as err:
  253. print(err)
  254. print("--------------------------------解析网页失败 %s失败 ----------------------------------" % url)
  255. continue
  256. if not row_id == 0:
  257. sql = "update %s set data = "%s" where id = %d " % (info_table, json_data, row_id)
  258. count -= 1
  259. else:
  260. movie_cursor.execute("select id from %s order by id desc limit 1 " % info_table)
  261. last_id = movie_cursor.fetchall()[0][0] # NOTE 获取最后一条数据的id
  262. sql = "INSERT INTO %s (id,data, createTime) VALUES (%d , "%s", %d)" % (
  263. info_table, last_id + 1, json_data, round(time.time() * 1000))
  264. try:
  265. movie_cursor.execute(sql)
  266. mask_tmp.commit()
  267. except Exception as err:
  268. print(err)
  269. print("---------------------------%s在写入数据库过程中出现问题 , 已回滚 ! -------------------------------" % url)
  270. mask_tmp.rollback() # NOTE 出错则回滚
  271. continue
  272. print("恭喜! 《%s》已成功入库啦!" % url)
  273. count += 1
  274. mask_tmp.close() # TODO 别忘了这个!
  275. print("恭喜您入库完成 ! 本次新入库%d条数据,用时%d秒" % (count, time.time() - start_time))
  276. print("------------------------华丽的分割线-----------------------------")
  277. print("即将扫描生成的图片.............................................................................................倒计时5")
  278. print("即将扫描生成的图片.............................................................................................倒计时4")
  279. print("即将扫描生成的图片.............................................................................................倒计时3")
  280. print("即将扫描生成的图片.............................................................................................倒计时2")
  281. print("即将扫描生成的图片.............................................................................................倒计时1")
  282. print("开始扫描生成的图片!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  283. frisk_image(cover_folder, cover_table) #调用图片处理
  284. frisk_image(avatar_folder, avatar_table) #调用图片处理

经实测,效果显著,百试不爽几乎完美适配所有豆瓣电影!!图书与音乐类似,在此不再赘述!感谢阅读!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41880.html

相关文章

  • 零基础如何学爬虫技术

    摘要:楚江数据是专业的互联网数据技术服务,现整理出零基础如何学爬虫技术以供学习,。本文来源知乎作者路人甲链接楚江数据提供网站数据采集和爬虫软件定制开发服务,服务范围涵盖社交网络电子商务分类信息学术研究等。 楚江数据是专业的互联网数据技术服务,现整理出零基础如何学爬虫技术以供学习,http://www.chujiangdata.com。 第一:Python爬虫学习系列教程(来源于某博主:htt...

    KunMinX 评论0 收藏0
  • 爬虫 - 收藏集 - 掘金

    摘要:在这之前,还是有必要对一些概念超轻量级反爬虫方案后端掘金前言爬虫和反爬虫日益成为每家公司的标配系统。 爬虫修炼之道——从网页中提取结构化数据并保存(以爬取糗百文本板块所有糗事为例) - 后端 - 掘金欢迎大家关注我的专题:爬虫修炼之道 上篇 爬虫修炼之道——编写一个爬取多页面的网络爬虫主要讲解了如何使用python编写一个可以下载多页面的爬虫,如何将相对URL转为绝对URL,如何限速,...

    1fe1se 评论0 收藏0
  • 首次公开,整理12年积累博客收藏夹,零距离展示《收藏夹吃灰》系列博客

    摘要:时间永远都过得那么快,一晃从年注册,到现在已经过去了年那些被我藏在收藏夹吃灰的文章,已经太多了,是时候把他们整理一下了。那是因为收藏夹太乱,橡皮擦给设置私密了,不收拾不好看呀。 ...

    Harriet666 评论0 收藏0
  • Nodejs爬虫--抓取豆瓣电影网页数据(下)

    摘要:接着上篇爬虫抓取豆瓣电影网页数据上本篇主要描述将上次抓取的数据存入数据库前提百度或谷歌的安装教程,安装本地并成功运行推荐一款数据库可视化管理工具。 接着上篇 Nodejs爬虫--抓取豆瓣电影网页数据(上) 本篇主要描述将上次抓取的数据存入mongodb数据库 前提:百度或谷歌mongodb的安装教程,安装本地并成功运行 推荐一款mongodb数据库可视化管理工具:Robomongo...

    legendaryedu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<