两个解析 HTML 文档的有力工具:lxml & BeautifulSoup4,一切暴露在互联网中的数据,都不是绝对安全的,但绝对是需要费脑筋才需要得到的,爬虫很简单学,真正难的是反爬。
urllib
-
get 请求
from urllib import request # 请求的 URL(百度翻译页面的爬取) url = 'https://fanyi.baidu.com/' # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/72.0.3626.121 Safari/537.36', } # 封装 URL 、请求头,并得到新的请求信息 req req = request.Request(url=url, headers=headers, ) # 根据拿到的请求信息,进行数据爬取,并接收响应值 ret = request.urlopen(req) # 拿到返回值进行读取,解码并打印 print(ret.read().decode())
-
post 请求
from urllib import request, parse # 请求的 URL url = 'https://fanyi.baidu.com/sug' # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/72.0.3626.121 Safari/537.36', } # POST 请求数据(bytes) data = parse.urlencode( { 'kw': '你好' } ).encode() # 封装 URL 、请求头,并得到新的请求信息 req req = request.Request(url=url, headers=headers, data=data) # 根据拿到的请求信息,进行数据爬取,并接收响应值 ret = request.urlopen(req) # 拿到返回值进行读取,解码并打印 print(ret.read().decode())
requests
requests 模块常用方法
单次请求(每发送一次请求,就需要调用一次)
- get()
- post()
- request()
多次请求(可以重复调用任何请求方法)
Session()
多用于 COOKIE 操作,会自动更新 COOKIE,需要实例化一个对象
以上两种所包含的任何方法或者类,除去 get() 和 post() 等,以请求类型命名的方法不需要传递请求类型这个参数,对于其他的则需要。其他参数都可以传递,比如(
#
号标记是必须参数):
- #method >
String
:请求类型(GET, POST…)- #url >
String
:请求地址- *params >
Dictionary
:GET 请求所携带的数据- *data >
Dictionary
:POST 请求所携带的数据- *json:
- *headers >
Dictionary
:请求头信息- *cookies >
Dictionary or CookieJar
:请求时所携带的 COOKIE,- *files:
- *auth:
- *timeout >
Float or Tuple
:请求超时时间- *allow_redirects:
- *proxies >
Dictionary
:请求代理- *verify >
Boolean
:- *stream:
- *cert:
-
get 请求
import requests # 请求的 URL url = 'https://www.baidu.com/s' # GET 请求所需数据 params = { 'wd': 'ip', 'ie': 'utf-8', } # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/72.0.3626.121 Safari/537.36', } # 开始一个 GET 请求 res = requests.get(url=url, params=params, headers=headers) # 对返回的数据进行解码 res.encoding = res.apparent_encoding # 输出内容 print(res.text)
对于 GET 请求的数据传递方式有两种,以上代码中使用的是其中一种,就是将数据封装到一个字典中,第二种就是直接拼装到 URL 中,如以下:
url = 'https://www.baidu.com/s?ie=utf-8&wd=ip'
即可省去
params
。 -
post 请求
import requests # 请求的 URL url = 'https://accounts.douban.com/j/mobile/login/basic' # POST 请求所需数据 data = { 'ck': '', 'name': '账号', 'password': '密码', 'remember': False, 'ticket': '', } # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/72.0.3626.121 Safari/537.36', } # 开始一个 POST 请求(登录豆瓣) res = requests.post(url=url, data=data, headers=headers) # 对返回的数据进行解码 res.encoding = res.apparent_encoding # 输出内容 print(res.text) # 验证是否登录成功,并携带 POST 请求响应时的 COOKIE res = requests.get('https://www.douban.com/mine/', headers=headers, cookies=res.cookies) # 输出内容 print(res.text)
-
代理
import requests # 请求的 URL url = 'http://www.baidu.com/s' # GET 请求所需数据 params = { 'wd': 'ip', 'ie': 'utf-8', } # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/72.0.3626.121 Safari/537.36', } # 代理 proxies = { 'http': '119.180.166.14:8060', } # 实例化一个 Session 对象 with requests.Session() as s: # 开始一个 GET 请求 res = s.request(method='GET', url=url, params=params, headers=headers, proxies=proxies) # 对返回的数据进行解码 res.encoding = res.apparent_encoding # 输出内容 print(res.text)
-
cookies
import requests # 请求的 URL url = 'https://accounts.douban.com/j/mobile/login/basic' # POST 请求所需数据 data = { 'ck': '', 'name': 17778022262, 'password': 'Yan092399.', 'remember': False, 'ticket': '', } # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/72.0.3626.121 Safari/537.36', } # 实例化一个 Session 对象 with requests.Session() as s: # 开始一个 POST 请求(登录豆瓣) res = s.request(method='POST', url=url, data=data, headers=headers) # 对返回的数据进行解码 res.encoding = res.apparent_encoding # 输出内容 print(res.text) # 验证是否登录成功,并携带 POST 请求响应时的 COOKIE res = requests.get('https://www.douban.com/mine/', headers=headers, cookies=s.cookies) # 输出内容 print(res.text)
scrapy
-
安装
python -m pip install scrapy
-
创建
# 创建一个项目 scrapy startproject Scrapy_Demo1 #创建一个爬虫 cd Scrapy_Demo1 scrapy genspider douban www.douban.com
spiders
: 存放爬虫文件
scrapy.cfg
: 项目的配置信息,主要为Scrapy命令行工具提供一个基础的配置信息
items.py
: 数据存储模板,用于结构化数据
middlewares.py
: 中间件,爬虫中间件,下载中间件,调度中间件
pipelines.py
: 数据处理行为,如:一般结构化的数据持久化
settings.py
: 配置文件 -
组件
- 引擎(Scrapy)
用来处理整个系统的数据流, 触发事务(框架核心) - 调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址 - 下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的) - 爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面 - 项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据 - 下载器中间件(Downloader Middlewares)
位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应 - 爬虫中间件(Spider Middlewares)
介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出 - 调度中间件(Scheduler Middewares)
介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应
- 引擎(Scrapy)
-
机制
- 引擎从调度器中取出一个链接(URL)用于接下来的抓取
- 引擎把URL封装成一个请求(Request)传给下载器
- 下载器把资源下载下来,并封装成应答包(Response)
- 爬虫解析Response
- 解析出实体(Item),则交给实体管道进行进一步的处理
- 解析出的是链接(URL),则把URL交给调度器等待抓取
-
简单爬取
定位到项目中的
settings.py
文件,修改以下值# 修改请求头 USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0' # 关闭爬虫规则 ROBOTSTXT_OBEY = False
编辑爬虫文件(
spiders/douban.py
)# -*- coding: utf-8 -*- class DoubanSpider(scrapy.Spider): # 爬虫名称 name = 'douban' # 爬虫范围 allowed_domains = ['www.douban.com'] # URL 列表 start_urls = ['http://www.douban.com/'] def parse(self, response): # 该方法主要用来解析页面内容 # 输出页面内容 print(response.text)
启动爬虫
# 带日志输出 scrapy crawl douban # 不带日志输出 scrapy crawl douban --nolog
页面内容的解析
# -*- coding: utf-8 -*- import scrapy from lxml import etree from bs4 import BeautifulSoup class DoubanSpider(scrapy.Spider): # 爬虫名称 name = 'douban' # 爬虫范围 allowed_domains = ['www.douban.com'] # URL 列表 start_urls = ['http://www.douban.com/'] def parse(self, response): # 1 scrapy 的 xpath 解析,获取热点图片链接 print( response.xpath('//div[@id="anony-sns"]//div[@class="albums"]//a/@href').extract() ) # or print( response.xpath('//div[@id="anony-sns"]//div[@class="albums"]//a/@href').getall() ) # 2 lxml 的 xpath 解析,获取热点文章链接 page_etree = etree.HTML(response.text) print( page_etree.xpath('//div[@id="anony-sns"]//div[@class="notes"]//a/@href') ) # 3 BeautifulSoup4 解析,获取热点话题链接 soup = BeautifulSoup(response.text) parents_div = soup.find('div', attrs={'id': 'anony-sns'}) link_list = parents_div.find_all('a', attrs={'class': 'rec_topics_name'}) print( list(map(lambda link: link.attrs['href'], link_list)) )
-
数据存储
改日