都9102年了,你还只会用python爬虫吗?

前言

无聊的时候,咱们总会打开豆瓣电影榜,从上到下刷着电影,仿佛那些经典台词立刻就在咱们耳边响起,如《肖申克的救赎》中:生活能够归结为一种简单的选择:不是忙于生存,就是赶着去死;《霸王别姬》中:说的是一生!差一年,一个月,一天,一个时辰,都不算一生!可是,豆瓣电影那么多,还有那么多页,一页一页的找岂不是太费时间了,今天我就来从前端的角度来爬取豆瓣电影榜top250的首页。豆瓣top250javascript

正文

1. 获取豆瓣首页的html

因为咱们须要向豆瓣的服务器发送一个请求来获得豆瓣首页的html代码,请求与响应在网络传输中须要必定的时间,然后续的操做须要在这个响应的基础上进行,所以,咱们须要将主函数定义为异步的。以上实现须要引入下面两个包(注意:request 是对等依赖,须要与request-promise 分开安装)html

npm i request
    npm i request-promise  
复制代码
let request = require('request-promise')
    const main = async() => {
      let html = await request({
        url'https://movie.douban.com/top250'
      })
      console.log(html)
    }
    main();
复制代码

2.对html结构分析

这样,咱们就获得了豆瓣首页的html代码,可是咱们须要的只是不多的一部分信息,因此咱们要对其中的信息进行提取,所以咱们引入一个npm包cheerio来提取html 特定选择器中的文本内容前端

npm i cherrio  
复制代码

而后在第二行引入java

let cheerio = require('cheerio');
复制代码

接下来咱们要作的就是利用cheerio 将获得的html文本载入内存构建DOM,并使用选择器对目标标签中的文本进行提取,所以咱们须要分析豆瓣首页的html结构node

content

content中是咱们须要的内容,gird-16-8 用于设置主体内容的布局,article是电影项的主体部分,咱们须要的内容就在其中,aside 置于网页右侧放置醒目信息,extra用于清除两端浮动。如今让咱们进入article中看看


咱们能够看到是一个典型的head,body,foot结构
opt mod 内放置一个复选框,用于用户选择根据条件筛选
grid_view 提取内容的主体
paginator 页尾的分页导航 进入grid_view,咱们所须要的内容都放置在一个一个的列表中,列表中的每个item都是一个咱们须要提取的一部电影。

3.使用选择器获得须要的内容

根据上面咱们对结构的分析,咱们就可使用下列语句获得咱们选择器内的每个itempython

let movieNodes = $('#content .article .grid_view').find('.item');
复制代码

而后咱们对保存了全部item的这样一个数组遍历,将其中每一项的关键值提取出来并保存,选择器的构建方法与上述的item得到的方法一致npm

let movies = [];
for (let i = 0; i < movieNodes.length; i++) {
    let $ = cheerio.load(movieNodes[i]);
    let titles = $('.info .hd span');
    titles = ([]).map.call(titles, t => $(t).text());
    let bd = $('.info .bd');
    let info = bd.find('p').text();
    let score = bd.find('.star .rating_num').text();
    movies.push({
      'titles': titles,
      'info': info,
      'score': score
    })
  }
复制代码

不一样的是,因为.hd 的选择器下面有四个span,所以咱们获得的是一个对象数组,咱们不能直接进行提取不然会出现对象循环引用的错误,咱们先使用map对空数组的每个元素进行映射,而后再经过改变this指针,使用选择器对空数组中每个元素进行赋值。而后将提取出来的每个字段append到数组中。编程

4.文件保存

接下来,咱们在第三行加上json

let fs = require('fs');
复制代码

在for循环的外面继续补充api

fs.writeFile('./mainjs.json'JSON.stringify(movies), 'utf-8', (err) => {
    if (err)
      console.log('写入失败');
    else
      console.log('写入成功');
  });
复制代码

就能够将咱们刚刚在存在数组中的数据以json格式写入文件中啦,打开这个文件就会发现豆瓣top250的首页内容就存到了这个文件中,咱们就能够挑选本身喜欢的电影观看了。 如今,让咱们回过头来看看豆瓣top250首页的页面结构

body豆瓣的首页由100%的body占据,body又分为上中下三部分,每一个版块又按照必定的功能再划分为相应的子模块,直到划分为一个最小的模块为止,每个模块占据网页的必定位置,实现某一个特定的功能,每一个模块之间互不干扰。

总结

经过对豆瓣的爬取,咱们知道了,若是一个网站的结构带有必定规律,并有良好的可读性,会让咱们的爬取更加简单,相反,若是一个网站乱糟糟的,毫无章法可言,那么这样的网站简直就是灾难。所以,咱们在从此的编程中,应当多看看一些大型网站的html代码,学习其中的结构设计和命名规范。而后咱们可使用BEM(Block Element Modifier)命名规范,对每个模块使用合适的选择器,这样不只提升了代码的可重用行,也利于网站的管理和维护。 暑假闲来无事看了点python视频,虽然已经忘得都差很少了,可是用了node爬取了一次,又让我又拾起的对python的热情,因而一番折腾,查了相关文档,终于给捣鼓出来了,毕竟,生命在于折腾嘛! 如下是python的爬虫方式,基本思路与上述爬虫方式相似,只是对数据处理的语法上有所差别:
须要安转requests和beautifulsoup4模块

pip install requests
pip install beautifulsoup4
复制代码
import requests
from bs4 import BeautifulSoup
import json
def gettext(x): 
    return x.get_text()
#设置请求头假装
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'}
# 先使用requests发送网络请求从而获取网页
html = requests.get('https://movie.douban.com/top250', headers=header)
# print(html.status_code)
# 传入html构建DOM
soup=BeautifulSoup(html.content,'html.parser')
movieNodes = soup.find('ol', attrs={'class': 'grid_view'})
movies=[]
for movie in movieNodes.find_all('li'):
    title=movie.find('div',attrs={'class':'hd'})
    titles=title.find_all('span')
    titles1=map(gettext,titles)
    tit=[]
    for ti in titles1:
      tit.append(ti)
    info=movie.find('p').get_text()
    score=movie.find('span',attrs={'class':'rating_num'}).get_text()
    movies.append({
      "titles": tit,
      "info": info,
      "score": score
    })
f = open("mainpy.json", "w", encoding='utf-8')
json.dump(movies, f, ensure_ascii=False)
f.close()
复制代码
  • 写的时候遇到了如下这些小问题
  1. 爬取豆瓣首页时,打印不出来html源码,查了一下才想起来是豆瓣作了反爬措施,须要假装一下请求头信息。因此之后咱们在爬取网站的时候能够先打印一下状态码status_code,若是显示418就说明被屏蔽了,显示200就说明请求成功。
  2. 使用find_all时,返回的是一个object数组,须要使用map方法提取出item 中每个span的内容,而后添加到数组中去。map方法的使用能够查看相关的文档,里面有对每个参数具体说明。
  3. 写文件的时候,报错说不能写入一个list,必须写入一个字符串,而后我将这个数组转成字符串写入文件,结果里面对转义字符没有进行转义,多了'\xa0','\n' 等,查了一下发现这个玩意儿'\xa0'居然就是html中的'&nbsp',而后我就用replace把每个这个字符给删掉,结果存是存进去了,文件全爆红色,对比了mainjs.json才发现,保存的文件mainpy.json不是一个Json类型的数据,而后又是一顿度娘,发现有个专门写json文件的api,json.dump(),而后我replace直接也不用写了,不过须要引入json库....

结束语

可能有时候写程序就是这样,出一个问题而后你就去解决这个问题,而后又出现另外的问题,你又得想怎么去解决新出现的问题,而后一直一直....结果发现直接使用一个api就行了...可是写程序注重的是过程,bug是咱们编程道路上的补品,咱们不能作一个apier,不是吗?

相关文章
相关标签/搜索