当前位置:   article > 正文

如何爬取猫眼全部信息(电影信息、演员信息)_手机版的猫眼怎么爬

手机版的猫眼怎么爬

爬取猫眼的全部信息,这里主要指的是电影列表里的电影信息和演员信息,如下界面。

       爬去的时候有两个难点。一:字体加密(如今好像机制有更新来些,用网上的方法不行);二:美团检测。下面将分别讲述我解决的过程。

一、字体加密

关于字体加密,网络上介绍的很多,思路也都类似。猫眼每次加载的时候会动态的加载不同的字形编码。解决思路呢,就是先下载好它的一个字体文件(.woff结尾的,方法网上多有介绍,我就不在累述了),然后每次爬取新的界面的时候,将新的字体文件的字形坐标与之前下载好的的对比。网上很多方法还是很久之前的,当时猫眼的字体加密机制还很简陋,只是很单纯的比对坐标是否相等,现在的机制是每次加载的字形都是有细微变化的。网上很多人的方法是说其变化在一个范围内,只要比对他们的差异不超过一个范围就是同一个数字。我也用了这个方法,但发现很多数字都识别错误,比如3,5,9;1,4;0,8;这三组数字很容易混淆。说明这个问题不是那么简单的。我用的方法先是取三个基准字体文件,然后将每个基准字体文件分别与动态加载的进行坐标对比,我也设置了一个差异范围,对符合这个差异的进行计数,最后计数最大的就认证为对应的数字。这样做之后还是不免出现错误,然后我发现每个数的坐标数目有差异,比如3,5,9这个三个数的坐标数量明显有很大不同,由于每个数字自身的坐标数每次加载也是不同的,有细微的差别,所以我再进行一个坐标个数差异判断。还有一点比较重要的,就是这个差异值的选择,网上很多选择的是8、10,可能猫眼加密机制改了的原因,这几个预设差异值,识别率很低。我试的时候 5 的识别率最高,这些你们可以自己实验。之前我们选了三组基准,由于我们进行了前面的识别操作,识别率进行很高了,所以再进行一次三局两胜的操作,再次提高识别率。由于代码比较多,我放一些关键的代码。还有一种方法也许可行,我没试过,可以参考这篇博客参考博文,这篇博客使用knn算法来识别的,识别率应该挺高的。但这个需要多准备些字体文件来提高识别率。

  1. def replace_font(self, response,res):
  2. #基准,比对三次,两次以上一致即默认正确
  3. #我是“我要出家当道士”,其他非原创
  4. base_font = TTFont('./fonts/base.woff')
  5. base_font.saveXML('./fonts/base_font.xml')
  6. base_dict = {'uniF870': '6', 'uniEE8C': '3', 'uniECDC': '7', 'uniE6A2': '1', 'uniF734': '5',
  7. 'uniF040': '9', 'uniEAE5': '0', 'uniF12A': '4', 'uniF2D2': '2', 'uniE543': '8'}
  8. base_list = base_font.getGlyphOrder()[2:]
  9. base_font2 = TTFont('./fonts/base2.woff')
  10. base_font2.saveXML('./fonts/base_font2.xml')
  11. base_dict2 = {'uniF230': '6', 'uniEBA1': '3', 'uniF517': '7', 'uniF1D2': '1', 'uniE550': '5',
  12. 'uniEBA4': '9', 'uniEB7A': '0', 'uniEC29': '4', 'uniF7E1': '2', 'uniF6B7': '8'}
  13. base_list2 = base_font2.getGlyphOrder()[2:]
  14. base_font3 = TTFont('./fonts/base3.woff')
  15. base_font3.saveXML('./fonts/base_font3.xml')
  16. base_dict3 = {'uniF8D3': '6', 'uniF0C9': '3', 'uniEF09': '7', 'uniE9FD': '1', 'uniE5B7': '5',
  17. 'uniF4DE': '9', 'uniF4F9': '0', 'uniE156': '4', 'uniE9B5': '2', 'uniEC6D': '8'}
  18. base_list3 = base_font3.getGlyphOrder()[2:]
  19. #网站动态加载的字体
  20. #我是“我要出家当道士”,其他非原创
  21. font_file = re.findall(r'vfile\.meituan\.net\/colorstone\/(\w+\.woff)', response)[0]
  22. font_url = 'http://vfile.meituan.net/colorstone/' + font_file
  23. #print(font_url)
  24. new_file = self.get_html(font_url)
  25. with open('./fonts/new.woff', 'wb') as f:
  26. f.write(new_file.content)
  27. new_font = TTFont('./fonts/new.woff')
  28. new_font.saveXML('./fonts/new_font.xml')
  29. new_list = new_font.getGlyphOrder()[2:]
  30. coordinate_list1 = []
  31. for uniname1 in base_list:
  32. # 获取字体对象的横纵坐标信息
  33. coordinate = base_font['glyf'][uniname1].coordinates
  34. coordinate_list1.append(list(coordinate))
  35. coordinate_list2 = []
  36. for uniname1 in base_list2:
  37. # 获取字体对象的横纵坐标信息
  38. coordinate = base_font2['glyf'][uniname1].coordinates
  39. coordinate_list2.append(list(coordinate))
  40. coordinate_list3 = []
  41. for uniname1 in base_list3:
  42. # 获取字体对象的横纵坐标信息
  43. coordinate = base_font3['glyf'][uniname1].coordinates
  44. coordinate_list3.append(list(coordinate))
  45. coordinate_list4 = []
  46. for uniname2 in new_list:
  47. coordinate = new_font['glyf'][uniname2].coordinates
  48. coordinate_list4.append(list(coordinate))
  49. index2 = -1
  50. new_dict = {}
  51. for name2 in coordinate_list4:#动态
  52. index2 += 1
  53. result1 = ""
  54. result2 = ""
  55. result3 = ""
  56. index1 = -1
  57. max = -1;
  58. for name1 in coordinate_list1: #本地
  59. index1 += 1
  60. same = self.compare(name1, name2)
  61. if same > max:
  62. max = same
  63. result1 = base_dict[base_list[index1]]
  64. index1 = -1
  65. max = -1;
  66. for name1 in coordinate_list2: #本地
  67. index1 += 1
  68. same = self.compare(name1, name2)
  69. if same > max:
  70. max = same
  71. result2 = base_dict2[base_list2[index1]]
  72. index1 = -1
  73. max = -1;
  74. for name1 in coordinate_list3: #本地
  75. index1 += 1
  76. same = self.compare(name1, name2)
  77. if same > max:
  78. max = same
  79. result3 = base_dict3[base_list3[index1]]
  80. if result1 == result2:
  81. new_dict[new_list[index2]] = result2
  82. elif result1 == result3:
  83. new_dict[new_list[index2]] = result3
  84. elif result2 == result3:
  85. new_dict[new_list[index2]] = result3
  86. else:
  87. new_dict[new_list[index2]] = result1
  88. for i in new_list:
  89. pattern = i.replace('uni', '&#x').lower() + ';'
  90. res = res.replace(pattern, new_dict[i])
  91. return res
  92. """
  93. 输入:某俩个对象字体的坐标列表
  94. #我是“我要出家当道士”,其他非原创
  95. 输出相似度
  96. """
  97. def compare(self, c1, c2):
  98. count = 0
  99. length1 = len(c1)
  100. length2 = len(c2)
  101. if abs(length2-length1) > 7:
  102. return -1
  103. length = 0
  104. if length1 > length2:
  105. length = length2
  106. else:
  107. length = length1
  108. #print(length)
  109. for i in range(length):
  110. if (abs(c1[i][0] - c2[i][0]) < 5 and abs(c1[i][1] - c2[i][1]) < 5):
  111. count += 1
  112. return count

二、美团防爬

关于美团防爬,网上也有很多博客,但内容大家应该知道的,你抄我,我抄你的,最后差不多都是一样的。但还是有很多优质的博文值得拜读。我水平也有限,给的参考借鉴也有限,所以我这里只给两种我自己用过的,而且成功爬取我需要的3000部电影和9000位演员数据。我是两种方法交织使用来爬取数据的,我使用了正常的requests来爬取和selenium自动工具(配合mitm——proxy)。速度最快的是requests,但很容易被检测;性能最稳定的是selenium,不易被检测。

1,正常的requests

     requests对于爬取猫眼还是很有用的,只是被美团检测后,需要很长时间的冷却,具体时间未知,我使用request配置已经登录过后的cookie成功爬取了全部的电影详情信息。参考代码如下。其实比较麻烦的就是xpath解析网页源码了。

  1. class getFilmsData(object):
  2. def __init__(self):
  3. self.headers = {}
  4. self.headers['User-Agent'] = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.06'
  5. self.headers['Cookie'] = '填写你的cookie,不知道的话,留言,我会快速告诉你'
  6. self.dataManager = mongoManager()
  7. self.fontDecode = FontDecode()
  8. #根据url,获取数据,
  9. #limitItem为限制页,即其页item之前的已经获取完成
  10. #我是“我要出家当道士”,其他非原创
  11. def getData(self,url,limitItem):
  12. s = requests.session()
  13. s.headers = self.headers
  14. s.keep_alive = False
  15. content = s.get(url).text
  16. #print("URLTEXT is :",response.text)
  17. if "验证中心" in content:
  18. print("目录界面美团验证")
  19. return False
  20. sel = etree.HTML(content)
  21. count = 0
  22. urls = sel.xpath('//div[@class="movie-item"]')
  23. scores = sel.xpath('//div[@class="channel-detail channel-detail-orange"]')
  24. for box in urls:
  25. #抓取列表界面的电影url
  26. count += 1
  27. if count < limitItem:
  28. continue
  29. print("begin ",count,"th item")
  30. scoreCheck = scores[count-1].xpath('.//text()')[0]
  31. #无评分的电影不爬取
  32. if scoreCheck == "暂无评分":
  33. break
  34. urlBack = box.xpath('.//a/@href')[0]
  35. #获取电影详情url
  36. url = "https://maoyan.com"+urlBack
  37. #获取电影名称、时长、上映日期、票房、评分、演员、海报url
  38. resp = s.get(url)
  39. realUrl = resp.url
  40. res = resp.text
  41. if "验证中心" in res:
  42. print("信息界面美团验证")
  43. return False
  44. #res2= self.replace_font(res)
  45. selTmp = etree.HTML(res)
  46. #电影票房
  47. #我是“我要出家当道士”,其他非原创
  48. money = selTmp.xpath('//div[@class="movie-index-content box"]/span[1]/text()')
  49. unit = selTmp.xpath('//div[@class="movie-index-content box"]/span[2]/text()')
  50. filmMoney = ""
  51. if len(money) == 0:
  52. #无票房的电影不爬取
  53. continue
  54. else:
  55. ascll = str(money[0])
  56. #print("money ascll is:",ascll)
  57. utfs = str(ascll.encode('unicode_escape'))[1:].replace("'","").replace("\\\\u",";&#x").split('.')
  58. unicode = ""
  59. if len(utfs)>1:
  60. unicode = utfs[0][1:]+";."+utfs[1][1:]+";"
  61. else:
  62. unicode = utfs[0][1:]+";"
  63. filmMoney = self.fontDecode.replace_font(res,unicode)
  64. if len(unit) > 0:
  65. filmMoney += unit[0]
  66. #电影名称
  67. filmName = selTmp.xpath('//div[@class="movie-brief-container"]/h1[1]/text()')[0]
  68. #电影海报
  69. filmImage = selTmp.xpath('//div[@class="avatar-shadow"]/img[1]/@src')[0]
  70. #电影时长
  71. filmTime = selTmp.xpath('//div[@class="movie-brief-container"]/ul[1]/li[2]/text()')[0].replace('\n', '').replace(' ', '')
  72. #电影上映时间
  73. filmBegin = selTmp.xpath('//div[@class="movie-brief-container"]/ul[1]/li[3]/text()')[0].replace('\n', '')
  74. #电影评分
  75. score = selTmp.xpath('//div[@class="movie-index-content score normal-score"]/span[1]/span[1]/text()')
  76. #由于票房和评分字体编码加密的缘故,所以需要先编码为unicode,在进行解密
  77. #我是“我要出家当道士”,其他非原创
  78. filmScore = ""
  79. if len(score) == 0:
  80. filmScore = "评分暂无"
  81. else:
  82. ascll = str(score[0])
  83. #print("score ascll is:",ascll)
  84. utfs = str(ascll.encode('unicode_escape'))[1:].replace("'","").replace("\\\\u",";&#x").split('.')
  85. unicode = ""
  86. if len(utfs)>1:
  87. unicode = utfs[0][1:]+";."+utfs[1][1:]+";"
  88. else:
  89. unicode = utfs[0][1:]+";"
  90. filmScore = self.fontDecode.replace_font(res,unicode)+"分"
  91. print(filmMoney,filmScore)
  92. #获取电影演员表,只获取前10个主要演员
  93. actorSol = selTmp.xpath('//div[@class="tab-celebrity tab-content"]/div[@class="celebrity-container"]/div[@class="celebrity-group"][2]/ul/li')
  94. #print(actors)
  95. actors = []
  96. actorUrls = []
  97. num = len(actorSol)
  98. for i in range(10):
  99. num -= 1
  100. if num < 0:
  101. break
  102. actorUrl = "https://maoyan.com"+actorSol[i].xpath('.//div[@class="info"]/a/@href')[0]
  103. actorItem = actorSol[i].xpath('.//div[@class="info"]/a/text()')[0].replace('\n', '').replace(' ', '')
  104. if len(actorSol[i].xpath('.//div[@class="info"]/span[1]/text()')) > 1:
  105. actorItem += (" "+actorSol[i].xpath('.//div[@class="info"]/span[1]/text()')[0].replace('\n', '').replace(' ', ''))
  106. actorUrls.append(actorUrl)
  107. actors.append(actorItem)
  108. #获取电影简介
  109. introductionT = ""
  110. introductionF = selTmp.xpath('//span[@class = "dra"]/text()')
  111. if len(introductionF) > 0:
  112. introductionT = introductionF[0]
  113. print(count,filmName,filmImage,filmBegin,filmTime,filmScore,filmMoney,actors,introductionT)
  114. time.sleep(1)
  115. s.close()

2,selenium配合mitmproxy

第二种方法就是selenium配合mitmproxy,selenium是一个自动化的工具,与request爬取网页内容不同,selenium可以模仿用户打开浏览器来浏览网页,所见的都可爬取。这个也多用在爬取大量数据避免检测时。但如果单纯的使用selenium,还是很容易被检测出来,这里我理解也不是特别深,只是单纯会用而已。大致就是浏览器设置的几个回复参数,如果使用selenium的话,这几个参数就会被赋值,而正常用户浏览的话,这几个参数是未定义的;还有一些其他的防selenium的,我没继续深入了解。

具体的配置方法可以参考我之前写的博客:mitmproxy配合selenium

我发现,我在爬取的时候,这个方法比第一个稳定,出现美团验证的几率很低,而且出现之后复制网站网址手动到对应的浏览器里打开,就会出现验证,你手动多试几次就过去了,然后就可以继续爬取。当然这个速度肯定没request快。举个例子吧,比如我在爬取https://maoyan.com/films/celebrity/28936的时候出现了美团检测,我用的是谷歌的驱动,那么复制这个网址到谷歌浏览器中,这时候一般会出现美团检测,你手动的验证通过后(不通过就关闭浏览器再来几次),再继续爬取就行了。其实selenium自动化使用浏览器还是被网址认为是人在浏览,只有出现检测的时候,才会被网址识别出是selenium,所以我们只要认为的过检测就可以了。

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

闽ICP备14008679号