当前位置:   article > 正文

2020-10-18 今天来说说如何爬取猫眼上的电影信息_猫眼提id

猫眼提id

今天来说说如何爬取猫眼上的电影信息

最近小编试图使用requests+BeautifulSoup取去抓取猫眼上的电影信息,但尝试一番后,发现输出的电影评分.评分人数和票房都是乱码.案例见下面代码.之后发现无论是使用BeautifulSoup还是正则表达式匹配出来的数据,他都是乱码的,不过使用正则表达式匹配出来的数据他和网页源代码倒是一致的,但我们想要的数字(若有兴趣可以查阅小编的文章,里面有专门的介绍如何破解这种字体方法),在我破解这种字体后,想着大开shajie,尽可能抓取.当我抓取到第32条电影时,突然报错.之后我用浏览器打开网址查看后,原来出现了滑动验证码,之后没办法小编又转用selenium+chrome去抓取,但是使用selenium抓取到的评分数据和下面用BeautifulSoup抓取的数据一样是乱码的,没法用.   之后转用api抓取就能够顺利解决问题了.

  1. import requests
  2. import re
  3. from bs4 import BeautifulSoup
  4. import random
  5. from fake_useragent import UserAgent
  6. headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
  7. 'Accept-Encoding': 'gzip, deflate, br',
  8. 'Accept-Language': 'zh-CN,zh;q=0.9',
  9. 'Connection': 'keep-alive',
  10. 'Host': 'maoyan.com',
  11. 'User-Agent':UserAgent().random,
  12. 'Referer': 'https://maoyan.com/',
  13. 'Cookie': '__mta=88838948.1602486634805.1602593489852.1602593548923.36; '
  14. 'uuid_n_v=v1; uuid=35B885800C5911EBABDB79CD76BA38F3E7502FF14A8E4FA8A0D25E551C5ACA0F;'
  15. ' _csrf=3412965d4c6fec458c7c07d87c59df17fc573baabc093ba1ac212e5a59a5904c; '
  16. '_lxsdk_cuid=1751ba58904c8-009e2816945f78-5040231b-144000-1751ba58904b0;'
  17. ' _lxsdk=35B885800C5911EBABDB79CD76BA38F3E7502FF14A8E4FA8A0D25E551C5ACA0F;'
  18. ' Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1602486635,1602498569;'
  19. ' _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; __mta=88838948.1602486634805.1602498583735.1602590114060.26;'
  20. ' Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1602593549; _lxsdk_s=17521d05fba-0a5-ad9-2ce%7C%7C32'
  21. }
  22. #使用session发起请求
  23. session = requests.Session()
  24. html = session.get('http://maoyan.com/films/246397',headers = headers)
  25. html.encoding = 'utf-8'
  26. bs = BeautifulSoup(html.text,'html.parser')
  27. name = bs.h1
  28. print(html)
  29. print('电影名 ',name.get_text())
  30. #使用BeautifulSoup取获取数据
  31. score = bs.select('div.movie-stats-container > div:nth-child(1) > div > span > span')[0].get_text()
  32. people = bs.find('span',{'class':'score-num'})
  33. print('BeautifulSoup评分: ',score)
  34. print('BeautifulSoup评分人数 ',people)
  35. acount = bs.select('div.movie-stats-container > div:nth-child(2) > div > span.stonefont')[0]
  36. print('BeautifulSoup票房 ',acount)
  37. #使用正则表达式在html.text中去匹配评分,评分人数以及票房
  38. people_pattern = re.compile('''<span class='score-num'><span class="stonefont">.*</span>人评分</span>''')
  39. acount_pattern = re.compile('''<span class="stonefont">.*</span><span class="unit">.*</span>''')
  40. score_pattern = re.compile('''<span class="index-left info-num ">\n <span class="stonefont">.*</span>''')
  41. people = people_pattern.findall(html.text)
  42. score = score_pattern.findall(html.text)
  43. acount = acount_pattern.findall(html.text)
  44. print('正则评分: ',score)
  45. print('正则评分人数 ',people)
  46. print('正则票房 ',acount)
  47. #以下是出书结果:
  48. <Response [200]>
  49. # 电影名 西游记之大圣归来
  50. # BeautifulSoup评分: .
  51. # BeautifulSoup评分人数 <span class="score-num"><span class="stonefont">.万###</span>人评分</span>
  52. # BeautifulSoup票房 <span class="stonefont">.</span>
  53. # 正则评分: ['<span class="index-left info-num ">\n <span class="stonefont">&#xe9ef;.&#xeae3;</span>']
  54. # 正则评分人数 ['<span class=\'score-num\'><span class="stonefont">&#xef27;&#xef27;&#xef27;.&#xeae3;万</span>人评分</span>']
  55. #正则票房 ['<span class="stonefont">&#xe9ef;.&#xf1bf;&#xea6a;</span><span class="unit">亿</span>']

一 利用API抓取猫眼电影

1.获取猫眼APi接口.     猫眼API接口详见下面URL http://blog.csdn.net/Anana13/article/details/107554897,这里小面只抓取电影信息,故只用了第七个接口电影详情:https://m.maoyan.com/ajax/detailmovie?movieId=1203734
参数:movieId ⇒ 电影ID

2.获取猫眼电影id.     我们知道它的API后,若再知道电影id后面就简单了.现在是获取电影id.在小编抓取到第67也后,下面程序就报错了.之后小编试图手动打开第68页,然而里面却没有任何内容.虽然它显示有几万页,没办法,小编就只能退而求其次,毕竟连普通用户都不能看到的数据,机器也不可能看得到(若有读者知道原因,小编愿闻其详),到这里小编爬取了1980个电影id

#注意在代码中我们每300存储一次links,所以,我们的json文件中实际只有1800个link,但我们的每爬取一个页面,就将该页面电影link输出,若为了完美,读者可将最后输出的180个电影写进去.到这为止我们得到了1980个电影的id,接下来就是使用api爬取

  1. import json
  2. import time
  3. import requests
  4. import re
  5. import random
  6. import pymysql
  7. from bs4 import BeautifulSoup
  8. import threading
  9. from fake_useragent import UserAgent
  10. from queue import Queue
  11. from selenium import webdriver
  12. from selenium.webdriver import ActionChains
  13. #驱动浏览器chrome
  14. driver = webdriver.Chrome(executable_path = r'C:\Users\Administrator\AppData\Local\Programs\Python\Python36\chromedriver.exe')
  15. #进入猫眼电影的第二页,读者也可以改为第一页或者其他页
  16. driver.get('http://maoyan.com/films?showType=3&offset=30')
  17. links = []
  18. while True:
  19. #找到div.movies-list > dl > dd下的所有电影内链接
  20. movies_list = driver.find_elements_by_css_selector('div.movies-list > dl > dd')
  21. for link in movies_list:
  22. #在一些大型网站上,可能会设有蜜獾(在浏览器界面不可见的内链接,但是在网页源代码中石村存在的,如果我们不小心访问了自己的ip可能会被封杀
  23. # link.is_displayed()可以避免这种情况,若link在浏览器界面是可见的(不是蜜獾),则返回True,若不可见(即为蜜獾,但又存在与html中)则返回False
  24. if link.is_displayed():
  25. #获取这个内链接的href属性,输出的形式为http://maoyan.com/films/1331267
  26. link = link.find_element_by_css_selector('div.movie-item.film-channel > a').get_attribute('href')
  27. try :
  28. #使用正则表达式提取films后的内容,形如films/1331267
  29. link = re.compile('films/\d+').search(link).group()
  30. #将其存入links中
  31. links.append(link)
  32. except :
  33. #匹配出错,则跳过
  34. pass
  35. print(len(links))
  36. print(links[-30:])
  37. #将存取的links写入links中,若我们每写一个就存入,则有点繁琐,若最后存取,若中间报错,则就白白存取了,
  38. #这里小编设置了每满300个,就存一次(读者可以自己设置)
  39. if len(links)%300 == 0:
  40. with open('maoyan.json','w') as fp:
  41. json.dump(links,fp)
  42. #让driver随机的休息几秒,毕竟若不休息,连续的抓取可能会被识别出是恶意的机器人,
  43. # 还有若网慢了,也可能会因为加载而报错
  44. time.sleep(random.choice([2.5,3.3,3.9,4.5,4.8,2.9,3.4]))
  45. #使用ActionChains模拟鼠标点击下一页的内链接
  46. ActionChains(driver).move_to_element(driver.find_element_by_link_text('下一页')).click().perform()
  47. #将窗口切换到在点击下一页后出现的窗口中
  48. windows = driver.window_handles
  49. driver.switch_to.window(windows[-1])
  50. #若我们提取的电影id超过10000则跳出循环,否则一直爬取
  51. if len(links) >= 10000:
  52. break
  53. #再次休息,尽可能取演的像真人
  54. time.sleep(random.choice([ 3.3, 3.9, 4.5, 4.8, 2.9, 3.4])+1)

3.抓取电影信息.电影抓取的步骤以及解释详见下面代码块

这里我们开启的线程,读者若不想开启线程,只需调用函数就可以.若读者需要存取数据,读者需要修改自己连接数据库参数更或者是表结构

 

  1. def get_movie_id():
  2. while True:
  3. #随机选取一个
  4. movie_id = random.choice(id)
  5. #每项的数字
  6. movie_id = movie_id[6:]
  7. if movie_id not in visited:
  8. visited.append(movie_id)
  9. break
  10. if len(visited) >= len(id)*0.99:
  11. #避免程序因为找最后几个,而一直进入死循环里(我们的选取是随机的)
  12. raise IndexError('max visited')
  13. return movie_id
  14. def print_data(item_id,item_nm,item_cat,item_distributions,
  15. item_sc,item_pubDesc,item_rt,item_oriLang,
  16. item_star,item_ver,item_dra):
  17. print('id ', item_id)
  18. print('电影名 ', item_nm)
  19. print('类型 ', item_cat)
  20. print('评分 ', item_distributions)
  21. print('评分 ', item_sc)
  22. print('上映地点 ', item_pubDesc)
  23. print('上映时间 ', item_rt)
  24. print('语种 ', item_oriLang)
  25. print('演员 ', item_star)
  26. print('放映版本 ', item_ver)
  27. print('简介 ', item_dra)
  28. def save_data():
  29. #连接数据库,读者需自己修改user,password,db,port以及Sql语句
  30. # 连接数据库,读者需自己修改user,password,db,port以及Sql语句
  31. # 连接数据库,读者需自己修改user,password,db,port以及Sql语句
  32. #重要事情说三遍
  33. conn = pymysql.connect(host = 'localhost',port = 3306,
  34. charset = 'utf8',db = 'scraping',
  35. user = 'root',password = 'luohong')
  36. cur = conn.cursor()
  37. while True:
  38. #若queue有东西就存入数据库
  39. if not queue.empty():
  40. try:
  41. value = queue.get()
  42. cur.execute('insert into maoyan1 values'
  43. '(%r,%r,%r,%r,%r,%r,%r,%r,%r,%r,%r)'%(value))
  44. conn.commit()
  45. except :
  46. #避免因为字符串长度不够等出现错误,而到导致爬虫错误
  47. pass
  48. def get_movie(thread_name):
  49. #i记录每个电影成功抓取的数据
  50. i = 1
  51. #监视循环
  52. k = 0
  53. while k <= 10000 :
  54. #将读取出的信息,进行正则解析,返回只包含数字的id
  55. movie_id = get_movie_id()
  56. #设置cookie以及headers,若读者运行此代码,需要用浏览器随便进入某个猫眼电影的界面,将requests下的cookie以及accept更换一下
  57. #需要注意的是这里小编用的是chrome浏览器,发现每个电影下的的cookie都是一样的,所以这里cookie没有更改
  58. #之前小编使用360去查看猫眼电信信息,的cookie是前面都不变,只是改变最后一位数字(1到100内的随机一个数字即可)
  59. #若读书者使用其他的浏览器的cookie,建议多打开几部,观察cookie的变化规律
  60. #只用一个也可以,但可能会很危险,但cookie每次报错(可能会出现验证,后续会说)或者重启程序后建议更新cookie
  61. headers = {
  62. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
  63. 'Accept-Encoding': 'gzip, deflate, br',
  64. 'Accept-Language': 'zh-CN,zh;q=0.9',
  65. 'Cache-Control': 'max-age=0',
  66. 'Connection': 'keep-alive',
  67. 'Host': 'm.maoyan.com',
  68. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
  69. 'Cookie': '_lxsdk_cuid=17522383e42c8-02edd93274060a-333376b-144000-17522383e42c8; _lxsdk=F82CAD200D5911EBA49537871E07CB9B88FA743AB2A44D3698ED5D9C226085C2; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1602596913,1602603790; uuid_n_v=v1; iuuid=337735D0102F11EB926F99D5C5A9852D9F6F125F17BC423AACEFA2A02E9EE547; ci=59%2C%E6%88%90%E9%83%BD; webp=true'
  70. }
  71. url = 'https://m.maoyan.com/ajax/detailmovie?movieId={}'.\
  72. format(movie_id)
  73. #输出线程的名字,正在访问的url
  74. print(thread_name,'------',url)
  75. html = requests.get(url,headers = headers )
  76. #模拟真人,随机休息,在抓取数据
  77. time.sleep(random.choice([1,1.5,1.8,2.3,2.6])+2.1)
  78. if html.status_code == 200:
  79. try :
  80. #若访问成功则对其json解析
  81. html_json = html.json()
  82. except:
  83. #有时会跳出滑动验证,会报错,这时我们pass
  84. pass
  85. else:
  86. #若try不报错(即解析成功),才运行else板块
  87. if len(html_json) > 0:
  88. for item in html_json:
  89. #赋值
  90. item_id = html_json[item].get('id', 'none')
  91. item_nm = html_json[item].get('nm', 'none')
  92. item_cat = html_json[item].get('cat', 'none')
  93. item_distributions = html_json[item].get('distributions', 'none')
  94. item_sc = html_json[item].get('sc', 'none')
  95. item_pubDesc = html_json[item].get('pubDesc', 'none')
  96. item_rt = html_json[item].get('rt', 'none')
  97. item_oriLang = html_json[item].get('oriLang','none')
  98. item_star = html_json[item]['shareInfo'].get('star','none')
  99. item_ver = html_json[item]['shareInfo'].get('ver','none')
  100. item_dra = html_json[item].get('dra', 'none')
  101. #打印数据
  102. print_data(item_id, item_nm, item_cat,
  103. item_distributions, item_sc,
  104. item_pubDesc, item_rt, item_oriLang,
  105. item_star, item_ver, item_dra)
  106. #将数据put进queue中
  107. try :
  108. queue.put((item_id, item_nm, item_cat,
  109. str(item_distributions), item_sc,
  110. item_pubDesc, item_rt, item_oriLang,
  111. item_star, item_ver, item_dra))
  112. except :
  113. pass
  114. #记录有效个数
  115. print(thread_name, i,'-------',len(visited))
  116. i += 1
  117. else:
  118. print('{}')
  119. else:
  120. print('error url')
  121. print("-------------------------------------------------")
  122. #随机休息
  123. time.sleep(random.choice([4.7,5.4,6,4.3,4.8,6.1,7.2,9.8,5.1,5.8]) + 5.4)
  124. k += 1
  125. #读取刚才的存放的maoyan.json文件
  126. with open('maoyan.json','r') as fp:
  127. content = json.load(fp)
  128. #这里我们开启了线程抓取数据,加快速度,所以使用了queue使得各个线程之间能够互通信息
  129. queue = Queue()
  130. #存取每个线程访问过的网站,避免其他线程再次抓取
  131. visited = []
  132. id = content.copy()[1000:]
  133. #若不想调用线程,则直接调用函数就可以了,不过特别慢
  134. #开启线程,之前小编没有开启线程,试着去抓取那速度很慢,时间过了半小时左右,就会报错(出现滑动验证)
  135. #在开启5个线程后,也是差不多半小时才报错,所以小编猜测他的反爬机制是达到半小时出现滑动验证,与数量无关
  136. #在我开启了15个线程后,避免报错重复抓取对id分成了0-500,500-1000,1000-1980三个区间抓取,就没有报错了,顺利抓取了,很幸运
  137. #读者可以尝试开启多线程抓取,若出现滑动验证,只需打开浏览器访问刚才出现滑动验证的url,手动验证后,重启程序就可以继续爬取(所以分区间很重要,和线程数量)
  138. threading.Thread(target = get_movie,args = ('THREAD1 ',)).start()
  139. threading.Thread(target = get_movie,args = ('THREAD2 ',)).start()
  140. threading.Thread(target = get_movie,args = ('THREAD3 ',)).start()
  141. threading.Thread(target = get_movie,args = ('THREAD4 ',)).start()
  142. threading.Thread(target = get_movie,args = ('THREAD5 ',)).start()
  143. threading.Thread(target = get_movie,args = ('THREAD6 ',)).start()
  144. threading.Thread(target = get_movie,args = ('THREAD7 ',)).start()
  145. threading.Thread(target = get_movie,args = ('THREAD8 ',)).start()
  146. threading.Thread(target = get_movie,args = ('THREAD9 ',)).start()
  147. threading.Thread(target = get_movie,args = ('THREAD10 ',)).start()
  148. threading.Thread(target = get_movie,args = ('THREAD11 ',)).start()
  149. threading.Thread(target = get_movie,args = ('THREAD12 ',)).start()
  150. threading.Thread(target = get_movie,args = ('THREAD13 ',)).start()
  151. threading.Thread(target = get_movie,args = ('THREAD14 ',)).start()
  152. threading.Thread(target = get_movie,args = ('THREAD15 ',)).start()
  153. #开启存储数据线成,若读者想将其存入自己的数据库,去掉#号就可以运行(不过需要输入自己的user,password,db等)
  154. # threading.Thread(target = save_data).start()

4.小编开启了15个线程后,差不多20分钟就可以搞定了,读者可以自行添加线程(厚道点,哈哈哈)

下面是部分结果

#由于小编边写代码,编写中文,所以符号之内的都用了英文符号,大家见谅

#若代码中有任何错误.希望大家可以积极提出,小编尽力修改

#转发者请标明文章出处

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/372016
推荐阅读
相关标签
  

闽ICP备14008679号