100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Python3网络爬虫开发实战

Python3网络爬虫开发实战

时间:2020-11-06 21:44:02

相关推荐

Python3网络爬虫开发实战

文章目录

第一章 爬虫基础1 HTTP基本原理1.1 URI和URL1.2 HTTP和HTTPS1.3 请求(Requset)1.3.1 请求方式1.3.2 请求的网址1.3.3 请求头:用来说明服务器要使用的附加信息1.3.4 请求体:一般承载的内容是POST请求中的表单数据,对于GET请求,请求体为空 1.4 响应(Response)1.4.1 响应状态码:表示服务器的响应状态1.4.2 响应头:包含服务器对请求的应答信息1.4.3 响应体:响应的正文数据都存在于响应体中 2 Web网页基础2.1 网页的组成2.2 页面的结构2.3 HTML节点树 3 爬虫的基本原理3.1 爬虫概述:获取网页并提取和保存信息的自动化程序3.2 JavaScript渲染的页面 4 Session和Cookie4.1 静态页面和动态页面4.2 无状态HTTP:HTTP协议对事务处理是没有记忆能力的4.3 Session4.4 Cookie4.4.1 会话Cookie和持久Cookie 5 代理的基本原理5.1 基本原理5.2 爬虫代理5.3 常见代理设置 6 多线程和多进程的基本原理6.1 多线程的含义6.2 多进程的含义6.3 并行和并发 第二章 基本库的使用1 urllib的使用1.1 发送请求(request模块)1.1.1 urlopen:方法read()得到响应的页面内容,属性status得到响应结果的状态码。url参数(必传)。1.1.2 Request1.1.3 高级用法1.1.4 Cookie 1.2 处理异常1.2.1 URLError类:继承自OSError类,是error异常模块的基类1.2.2 HTTPError类:是URLError的子类专门用来处理HTTP请求错误 1.3 解析链接1.3.1 urlparse:实现URL的识别和分段1.3.2 urlunparse:用于构造URL,接受的参数是一个可迭代对象,其长度必须是6。1.3.3 urlsplit:与urlparse方法类似,但其不再单独解析params这一部分(params会合并到path中),返回5个结果。1.3.4 urlunsplit:与urlunparse方法类似,唯一的区别是这里接受的参数的长度必须为5。1.3.5 urljoin:生成链接,提供一个base_url作为该方法的第一个参数,将新链接作为第二个参数,通过分析base_url的scheme、netloc和path这三部分,并对新链接缺失的内容进行补充。1.3.6 urlencode:将字典类型的数据转化为GET的请求参数1.3.7 parse_qs:将一串GET请求参数转回字典1.3.8 parse_qsl:将一串GET参数转化为由元组组成的列表1.3.9 quote:将中文字符转化为URL编码格式1.3.10 unquote:进行URL解码 1.4 分析Robots协议1.4.1 Robots协议:网络爬虫排除协议1.4.2 robotparser 2 requests的使用2.1 GET请求2.1.1 基本实例2.1.2 抓取网页2.1.3 抓取二进制数据2.1.4 添加请求头(headers参数) 2.2 POST请求(data参数)2.3 响应2.4 高级用法2.4.1 文件上传(files参数)2.4.2 设置Cookie(cookies参数)2.4.3 维持Session(Session对象)2.4.4 SSL证书验证2.4.5 设置超时2.4.6 身份认证(auth参数)2.4.7 设置代理 3 正则表达式3.1 match3.1.1 匹配目标3.1.2 贪婪匹配(.*):匹配尽可能多的字符3.1.3 非贪婪匹配(.*?):匹配尽可能少的字符3.1.4 匹配结果在中间尽量用非贪婪匹配,匹配结果在字符串结尾尽量用贪婪匹配。3.1.5 修饰符:控制匹配的模式3.1.6 转义匹配 3.2 search:扫描整个字符串,然后返回第一个匹配成功的结果。3.3 findall:获取与正则表达式相匹配的所有字符串3.4 sub:修改文本3.5 compile 4 基础爬虫案例实战4.1 爬取目标4.2 爬取列表页4.3 爬取详情页4.4 保存数据4.5 多进程加速 第三章 网页数据的解析提取1 XPath的使用2 Beautiful Soup的使用 第四章 数据的存储

第一章 爬虫基础

1 HTTP基本原理

1.1 URI和URL

URI:统一资源标识符(Uniform Resource Identifier)

URL:统一资源定位符(Uniform Resource Locator)

URN:统一资源名称(Uniform Resource Name)

1.2 HTTP和HTTPS

HTTP:超文本传输协议(Hypertext Transfer Protocol)

HTTPS:以安全为目标的HTTP通道(Hypertext Transfer Protocol)

1.3 请求(Requset)

1.3.1 请求方式

1.3.2 请求的网址

1.3.3 请求头:用来说明服务器要使用的附加信息

1.3.4 请求体:一般承载的内容是POST请求中的表单数据,对于GET请求,请求体为空

1.4 响应(Response)

1.4.1 响应状态码:表示服务器的响应状态

1.4.2 响应头:包含服务器对请求的应答信息

1.4.3 响应体:响应的正文数据都存在于响应体中

2 Web网页基础

2.1 网页的组成

HTML:超文本标记语言(Hypertext Markup Language),用来描述网页的语言

CSS:层叠样式表(Cascading Style Sheets)

JavaScript:简称JS,是一种脚本语言,实现一种实时、动态、交互的页面功能

2.2 页面的结构

2.3 HTML节点树

HTML节点树也叫HTML DOM树。DOM:文档对象模型(Document Object Model)。

3 爬虫的基本原理

3.1 爬虫概述:获取网页并提取和保存信息的自动化程序

获取网页的源代码分析源代码,从中提取我们想要的数据将提取到的数据保存在某处以便后续使用

3.2 JavaScript渲染的页面

4 Session和Cookie

4.1 静态页面和动态页面

静态页面:由HTML代码编写,文字、图片等内容均通过写好的HTML代码来指定

动态页面:动态解析URL中参数的变化,关联数据库并动态呈现不同的页面内容

4.2 无状态HTTP:HTTP协议对事务处理是没有记忆能力的

4.3 Session

Session对象用来存储特定用户Session所需的属性及配置信息。

Session在服务端,也就是网站的服务器,用来保存用户的Session信息。

4.4 Cookie

某些网站为了鉴别用户身份、进行Session跟踪而存储在用户本地终端上的数据。

Cookie在客户端,也可以理解为在浏览器端,有了Cookie,浏览器在下次访问相同网页时就会自动附带上它,并发送给服务器,服务器通过识别Cookie鉴别出是哪个用户在访问,然后判断此用户是否处于登录状态,并返回对应的响应。

4.4.1 会话Cookie和持久Cookie

会话Cookie就是把Cookie放在浏览器内存里,关闭浏览器之后,Cookie即失效。

持久Cookie把Cookie保存在客户端的硬盘中,下次还可以继续使用,用于长久保持客户的登录状态。

5 代理的基本原理

5.1 基本原理

代理实际上就是指代理服务器(Proxy Server),功能是代网络用户取得网络信息。

5.2 爬虫代理

使用代理隐藏真实的IP,让服务器以为是代理服务器在请求自己。

5.3 常见代理设置

高度匿名代理付费代理服务ADSL拨号蜂窝代理

6 多线程和多进程的基本原理

6.1 多线程的含义

一个进程中同时执行多个线程。

线程是操作系统进行运算调度的最小单元,是进程中的最小运行单元。

6.2 多进程的含义

同时运行多个进程。

进程是具有一定独立功能的程序在某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。

6.3 并行和并发

并发(concurrency)是指多个线程对应的多条指令被快速轮换的执行。

并行(parallel)指同一时刻有多条指令在多个处理器上同时执行。

第二章 基本库的使用

1 urllib的使用

1.1 发送请求(request模块)

1.1.1 urlopen:方法read()得到响应的页面内容,属性status得到响应结果的状态码。url参数(必传)。

"""抓取Python官网网页"""import urllib.requestresponse = urllib.request.urlopen('')print(response.read().decode('utf-8'))"""调用相关方法和属性获取相关信息"""print(response.status) # 响应的状态码print(response.getheaders()) # 获取多个同名请求头对应的一组value值,因此返回枚举类型数据print(response.getheader('Server')) # 获取单个请求头name对应的value值

data参数:添加该参数时,需要使用bytes方法将参数转化为字流节编码格式的内容,即bytes类型。若传递此参数,请求方式变为POST请求。

"""data参数,若传递此参数请求方式变为POST请求"""import urllib.parseimport urllib.request# urlencode方法将字典参数转换为字符串data = bytes(urllib.parse.urlencode({'name': 'germey'}), encoding='utf-8')response = urllib.request.urlopen('/post', data=data)print(response.read().decode('utf-8'))

timeout参数:用于设置超时时间,单位为秒。

import socketimport urllib.requestimport urllib.errortry:response = urllib.request.urlopen('/get', timeout=0.1)except urllib.error.URLError as e:if isinstance(e.reason, socket.timeout): #判断异常类型print('TIME OUT')

1.1.2 Request

url参数(必传)

data参数:传入bytes类型的数据

headers参数:字典类型,请求中的请求头。可以通过调用请求实例的add_header方法添加。

"""通过add_header方法添加header"""req = request.Request(url=url, data=data, method='POST')req.add_header('User-Agent', 'Mozilla/4.0 (compatible;MSIE 5.5;Windows NT)')

method参数:字符串,用来指示请求的方法。

from urllib import request, parseurl = '/post'headers = {'User-Agent': 'Mozilla/4.0 (compatible;MSIE 5.5;Windows NT)','Host': ''}dict = {'name': 'germey'}data = bytes(parse.urlencode(dict), encoding='utf-8')req = request.Request(url=url, data=data, headers=headers, method='POST')response = request.urlopen(req)print(response.read().decode('utf-8'))

1.1.3 高级用法

验证(HTTPBasicAuthHandler模块):网站启用了基本身份认真,允许页面浏览器或者其他客户端程序在请求网站时提供用户名和口令形式的身份凭证。

from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_openerfrom urllib.error import URLErrorusername = 'admin'password = 'admin'url = 'https://ssr3.scrape.center/'p = HTTPPasswordMgrWithDefaultRealm()p.add_password(None, url, username, password)auth_handler = HTTPBasicAuthHandler(p) # 实例化一个HTTPBasicAuthHandler对象opener = build_opener(auth_handler)try:result = opener.open(url)html = result.read().decode('utf-8')print(html)except URLError as e:print(e.reason)

代理(ProxyHandler模块)

from urllib.error import URLErrorfrom urllib.request import ProxyHandler, build_openerproxy_handler = ProxyHandler({'http': 'http://127.0.0.1:8080','https': 'https://127.0.0.1:8080'})opener = build_opener(proxy_handler)try:response = opener.open('')print(response.read.decode('utf-8'))except URLError as e:print(e.reason)

1.1.4 Cookie

获取Cookie:CookieJar对象

import http.cookiejar, urllib.requestcookie = http.cookiejar.CookieJar()handler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(handler)response = opener.open('')for item in cookie:print(item.name + "=" + item.value)

保存Cookie

Mozilla格式:MozillaCookieJar对象

import urllib.request, http.cookiejarfilename = 'cookie.text'cookie = http.cookiejar.MozillaCookieJar(filename)handler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(handler)response = opener.open('')cookie.save(ignore_discard=True, ignore_expires=True)

LWP格式:LWPCookieJar对象

cookie = http.cookiejar.LWPCookieJar(filename)

读取Cookie(以LWPCookieJar格式为例)

import urllib.request, http.cookiejarcookie = http.cookiejar.LWPCookieJar()cookie.load('cookie1.text', ignore_discard=True, ignore_expires=True)handler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(handler)response = opener.open('')print(response.read().decode('utf-8'))

1.2 处理异常

1.2.1 URLError类:继承自OSError类,是error异常模块的基类

属性reason:返回错误的原因。

from urllib import request, errortry:response = request.urlopen('/404')except error.URLError as e:print(e.reason)

1.2.2 HTTPError类:是URLError的子类专门用来处理HTTP请求错误

from urllib import request, errortry:response = request.urlopen('/404')except error.HTTPError as e:print(e.reason, e.code, e.headers, sep='\n')

因为URLError是HTTPError的父类,所以可以选择先捕获子类的错误,再捕获父类的错误

from urllib import request, errortry:response = request.urlopen('/404')except error.HTTPError as e:print(e.reason, e.code, e.headers, sep='\n')except error.URLError as e:print(e.reason)else:print('Request Successfully')

1.3 解析链接

1.3.1 urlparse:实现URL的识别和分段

from urllib.parse import urlparseresult = urlparse('/index.html;user?id=5#comment')print(result)

解析结果是一个元组,既可以通过属性名获取内容,也可以用索引顺序获取。

"""ParseResult实际上是一个元组"""print(result.scheme, result[0], loc, result[1], sep='\n')

1.3.2 urlunparse:用于构造URL,接受的参数是一个可迭代对象,其长度必须是6。

from urllib.parse import urlunparsedata = ['https', '', 'index.html', 'user', 'a=6', 'comment']print(urlunparse(data))

1.3.3 urlsplit:与urlparse方法类似,但其不再单独解析params这一部分(params会合并到path中),返回5个结果。

from urllib.parse import urlsplitresult = urlsplit('/index.html;user?id=5#comment')print(result)"""SplitResult返回的也是一个元组"""print(result.scheme, result[0])

1.3.4 urlunsplit:与urlunparse方法类似,唯一的区别是这里接受的参数的长度必须为5。

from urllib.parse import urlunsplitdata = ['https', '', 'index.html', 'a=6', 'comment']print(urlunsplit(data))

1.3.5 urljoin:生成链接,提供一个base_url作为该方法的第一个参数,将新链接作为第二个参数,通过分析base_url的scheme、netloc和path这三部分,并对新链接缺失的内容进行补充。

from urllib.parse import urljoinprint(urljoin('', 'FAQ.html'))print(urljoin('', '/FAQ.html'))print(urljoin('/about.html', '/FAQ.html?question=2'))print(urljoin('?wd=abc', '/index.php'))print(urljoin('', '?category=2#comment'))print(urljoin('#comment', '?category=2'))

1.3.6 urlencode:将字典类型的数据转化为GET的请求参数

from urllib.parse import urlencodeparams = {'name':'germey','age':25}base_url = '?'url = base_url + urlencode(params)print(url)

1.3.7 parse_qs:将一串GET请求参数转回字典

from urllib.parse import parse_qsquery = 'name=germey&age=25'print(parse_qs(query))

1.3.8 parse_qsl:将一串GET参数转化为由元组组成的列表

from urllib.parse import parse_qslprint(parse_qsl(query))

1.3.9 quote:将中文字符转化为URL编码格式

from urllib.parse import quotekeyword = '壁纸'url = '/s?wd=' + quote(keyword)print(url)

1.3.10 unquote:进行URL解码

from urllib.parse import unquoteprint(unquote(url))

1.4 分析Robots协议

1.4.1 Robots协议:网络爬虫排除协议

1.4.2 robotparser

set_url:用来设置robots.txt文件的链接,如果在创建RobotFileParse对象时传入了链接就不需要此方法来设置。

rp = RobotFileParser('/robots.txt')

read:读取robot.txt文件并进行分析,一定要调用这个方法。

parse:用来解析robots.txt文件,传入的参数是robots.txt文件中的某些行内容,它会按照robots.txt的语法规则来分析这些内容。

from urllib.request import urlopenfrom urllib.robotparser import RobotFileParserrp = RobotFileParser()rp.parse(urlopen('/robots.txt').read().decode('utf-8').split('\n'))print(rp.can_fetch('Baiduspider', ''))print(rp.can_fetch('Baiduspider', '/homepage/'))print(rp.can_fetch('Googlebot', '/homepage/'))

can_fetch:该方法有两个参数,第一个是User-Agent,第二个是要抓取的URL,表示User-Agent指示的搜索引擎是否可以抓取这个URL。

from urllib.robotparser import RobotFileParserrp = RobotFileParser()rp.set_url('/robots.txt')rp.read()print(rp.can_fetch('Baiduspider', ''))print(rp.can_fetch('Baiduspider', '/homepage/'))print(rp.can_fetch('Googlebot', '/homepage/'))

2 requests的使用

2.1 GET请求

2.1.1 基本实例

import requestsr = requests.get('/get')print(r.text)

利用params参数可以直接传递参数

import requestsdata = {'name':'germey','age':25}r = requests.get('/get', params=data)print(r.text)

网页的返回类型虽然是str类型,但是它很特殊,是JSON格式的。

import requestsr = requests.get('/get')print(type(r.text))print(r.json())print(type(r.json()))

2.1.2 抓取网页

import requestsimport rer = requests.get('https://ssr1.scrape.center/')pattern = pile('<h2.*?>(.*?)</h2>', re.S) # 正则表达式titles = re.findall(pattern, r.text)print(titles)

2.1.3 抓取二进制数据

import requestsr = requests.get('https://ssr1.scrape.center/static/img/logo.png')with open('logo.png', 'wb') as fp:fp.write(r.content)

2.1.4 添加请求头(headers参数)

import requestsheaders = {'User-Agent':'Mozilla/4.0 (compatible;MSIE 5.5;Windows NT)'}r = requests.get('https://ssr1.scrape.center/', headers=headers)print(r.text)

2.2 POST请求(data参数)

import requestsdata = {'name':'germey', 'age':25}r = requests.post('/post', data=data)print(r.text)

2.3 响应

import requestsr = requests.get('https://ssr1.scrape.center/')print(type(r.status_code), r.status_code)print(type(r.headers), r.headers)print(type(r.cookies), r.cookies)print(type(r.url), r.url)print(type(r.history), r.history) # 请求历史

2.4 高级用法

2.4.1 文件上传(files参数)

import requestsfiles = {'file':open('logo.png', 'rb')}r = requests.post('/post', files=files)print(r.text)

2.4.2 设置Cookie(cookies参数)

获取Cookie

import requestsr = requests.get('')print(r.cookies)for key, value in r.cookies.items():print(key + '=' + value)

维持Cookie

第一种,将Cookie设置到请求头中,然后发送请求。

第二种,先构造一个RequsetsCookieJar对象,然后对Cookie进行处理和赋值,传递给cookies参数。

2.4.3 维持Session(Session对象)

利用Session可以做到模拟同一个会话而不用担心Cookie的问题,它通常在模拟登录成功之后,进行下一步操作时用到。

Session在平常用的非常广泛,可以用于模拟在一个浏览器中打开同一站点的不同画面。

"""Session维持小实验"""import requestsrequests.get('/cookies/set/number/123456789') # 请求一个测试网站r = requests.get('/cookies') # 获取当前的Cookie信息print(r.text) # 不能成功获取设置的Cookie"""Session维持实例(维持同一个Session)"""s = requests.Session() # 创建一个Session对象s.get('/cookies/set/number/123456789')r = s.get('/cookies')print(r.text)

2.4.4 SSL证书验证

使用verify参数控制是否验证证书,如果将此参数设置为False,那么在请求时就不会再验证证书是否有效。

"""SSL证书验证小实验"""import requestsresponse = requests.get('https://ssr2.scrape.center/')print(response.status_code) # 报错"""SLL证书验证跳过(verify参数)"""response = requests.get('https://ssr2.scrape.center/', verify=False)print(response.status_code) # 不报错,出现警告"""设置忽略警告的方式屏蔽这个警告"""import requestsfrom requests.packages import urllib3urllib3.disable_warnings() # 忽略警告response = requests.get('https://ssr2.scrape.center/', verify=False)print(response.status_code)"""通过捕获警告到日志的方式忽略警告"""import loggingimport requestslogging.captureWarnings(True) # 捕获警告response = requests.get('https://ssr2.scrape.center/', verify=False)print(response.status_code)

2.4.5 设置超时

为了防止服务器不能及时响应,应该设置一个超时时间,如果超过这个时间还没有得到响应,就报错。

使用timeout参数,其值时发出请求再到服务器返回响应的时间。

"""超时设置(timeout参数)"""import requestsr = requests.get('/get', timeout=1)print(r.status_code)"""timeout参数(连接时间, 读取时间)"""import requestsr = requests.get('/get', timeout=(5, 30))print(r.status_code)

2.4.6 身份认证(auth参数)

"""身份认证(auth参数)"""import requestsfrom requests.auth import HTTPBasicAuthr = requests.get('https://ssr3.scrape.center/', auth=HTTPBasicAuth('admin', 'admin'))print(r.status_code)"""身份认证简化版"""import requestsr = requests.get('https://ssr3.scrape.center/', auth=('admin', 'admin'))print(r.status_code)

2.4.7 设置代理

进行大规模爬取,面对大规模爬取且频发的请求时,网站就可能弹出验证码,或者跳转到登陆认证界面,更甚者可能会封禁客户端的IP,为了防止这种情况发生,我们需要设置代理来解决这个问题,需要使用到proxies参数。

from requests import Request,Sessionurl = '/post'data = {'name':'germey'}headers = {'User-Agent':'Mozilla/4.0 (compatible;MSIE 5.5;Windows NT)'}s = Session()req = Request('POST', url, data=data, headers=headers) # 构造一个Request对象prepped = s.prepare_request(req) # 转换为一个Prepared Request对象r = s.send(prepped) # 发送请求print(r.text)

3 正则表达式

3.1 match

传入要匹配的字符串以及正则表达式,从字符串开始位置检测正则表达式是否和字符串相匹配。返回对象包含两个方法,group方法输出匹配到的内容,span方法输出匹配的范围。

import recontent = 'Hello 123 4567 World_This is a Regex Demo'print(len(content))# '^' 匹配字符串的开头result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}', content)print(result)print(result.group())print(result.span())

3.1.1 匹配目标

使用括号()将想提取的字符串括起来,调用group方法传入分组的索引即可获得提取结果。

import recontent = 'Hello 1234567 World_This is a Regex Demo'result = re.match('^Hello\s(\d+)\sWorld', content)print(result.group(1))

3.1.2 贪婪匹配(.*):匹配尽可能多的字符

import recontent = 'Hello 1234567 World_This is a Regex Demo'# '$' 结尾字符串result = re.match('^He.*(\d+).*Demo$', content)print(result.group(1))

3.1.3 非贪婪匹配(.*?):匹配尽可能少的字符

import recontent = 'Hello 1234567 World_This is a Regex Demo'# '?' 英文格式下的问号result = re.match('^He.*?(\d+).*Demo$', content)print(result.group(1))

3.1.4 匹配结果在中间尽量用非贪婪匹配,匹配结果在字符串结尾尽量用贪婪匹配。

import recontent = '/comment/kEraCN'result1 = re.match('http.*?comment/(.*?)', content)result2 = re.match('http.*?comment/(.*)', content)print('result1',result1.group(1))print('result2',result2.group(1))

3.1.5 修饰符:控制匹配的模式

re.I:使匹配对大小写不敏感

re.M:多行匹配,影响^和$

re.S:使匹配内容包括换行符在内的所有字符

import recontent = '''Hello 1234567 World_Thisis a Regex Demo'''result = re.match('^He.*?(\d+).*?Demo$', content, re.S)print(result.group(1))

3.1.6 转义匹配

当目标字符串中遇到用作用作正则匹配模式的特殊字符时,在此字符前面加反斜线\转义一下即可。

import recontent = '(百度)'result = re.match('\(百度\)www\.baidu\.com', content)print(result)

3.2 search:扫描整个字符串,然后返回第一个匹配成功的结果。

import recontent = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'result = re.search('He.*?(\d+).*?Demo', content)print(result.group(1))

3.3 findall:获取与正则表达式相匹配的所有字符串

3.4 sub:修改文本

import recontent = '54aK54yr5oiR54ix5L2g'content = re.sub('\d+', '', content)print(content)

3.5 compile

将正则字符串编译成正则表达式对象,以便在后面的匹配中复用。

compile中可以传入修饰符,在使用search、findall方法时就不必额外传,给正则表达式做了一层封装。

import recontent1 = '-12-15 12:00'content2 = '-12-17 12:55'content3 = '-12-22 13:21'pattern = pile('\d{2}:\d{2}')result1 = re.sub(pattern, '', content1)result2 = re.sub(pattern, '', content2)result3 = re.sub(pattern, '', content3)print(result1, result2,result3)

4 基础爬虫案例实战

4.1 爬取目标

爬取站点每一页的电影列表并爬取每个电影的详情页用正则提取每一部电影的名称、封面、类别、上映时间、评分、剧情简介等将数据保存为JSON文本文件利用多线程实现爬取的加速

4.2 爬取列表页

遍历所有页码,构造10页的URL从每个索引页提取出电影详情页的URL

import requestsimport logging # 输出信息import refrom urllib.parse import urljoinlogging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s: %(message)s')BASE_URL = 'https://ssr1.scrape.center'TOTAL_PAGE = 10def scrape_page(url): # 通用的爬取页面的方式logging.info('scraping %s...',url)try:response = requests.get(url)if response.status_code == 200: # 爬取成功return response.text# 爬取失败 输出错误日志信息logging.error('get invalid status code %s while scraping %s',response.status_code,url)except requests.RequestException: # requests的异常处理logging.error('error occurred while scraping %s',url,exc_info=True)def scrape_index(page): # 列表页的爬取index_url = f'{BASE_URL}/page/{page}'return scrape_page(index_url)def parse_index(html): # 解析列表页pattern = pile('<a.*?href="(.*?)".*?class="name">')items = re.findall(pattern, html)if not items: # 匹配失败return []for item in items:detail_url = urljoin(BASE_URL,item)logging.info('get detail url %s',detail_url)yield detail_url

4.3 爬取详情页

def scrape_detail(url): # 爬取详情页return scrape_page(url)def parse_detail(html): # 解析详情页# 封面cover_pattern = pile('class="item.*?<img.*?src="(.*?)".*?class="cover">',re.S)if re.search(cover_pattern, html):cover = re.search(cover_pattern, html).group(1).strip() else:cover = None# 名称name_pattern = pile('<h2.*?>(.*?)</h2>')if re.search(name_pattern, html):name = re.search(name_pattern, html).group(1).strip()else:name = None# 类别categories_pattern = pile('<button.*?category.*?<span>(.*?)</span>.*?</button>',re.S)if re.findall(categories_pattern,html):categories = re.findall(categories_pattern,html)else:categories = []# 上映时间published_at_pattern = pile('(\d{4}-\d{2}-\d{2})\s?上映')if re.search(published_at_pattern,html):published_at = re.search(published_at_pattern,html).group(1)else:published_at = None# 剧情简介drama_pattern = pile('<div.*?drama.*?>.*?<p.*?>(.*?)</p>',re.S)if re.search(drama_pattern,html):drama = re.search(drama_pattern,html).group(1).strip() else:drama = None# 评分score_pattern = pile('<p.*?score.*?>(.*?)</p>',re.S)if re.search(score_pattern,html):score = float(re.search(score_pattern,html).group(1).strip()) else:score = None return {'cover':cover, 'name':name, 'categories':categories,'published_at':published_at, 'drama':drama, 'score': score}

4.4 保存数据

import jsonfrom os import makedirsfrom os.path import existsRESULTS_DIR = 'results' # 保存文件夹exists(RESULTS_DIR) or makedirs(RESULTS_DIR) # 不存在就创建def save_data(data): # 保存数据name = data.get('name')[:4] # 有非法字符data_path = f'{RESULTS_DIR}/{name}.json' # 文件路径# ensure_ascii 确保中文字符正常呈现 indent 缩进json.dump(data,open(data_path,'w',encoding='utf-8'),ensure_ascii=False,indent=2)

4.5 多进程加速

import multiprocessingdef main(page):index_html = scrape_index(page)detail_urls = parse_index(index_html)# logging.info('detail urls %s', list(detail_urls))for detail_url in detail_urls:detail_html = scrape_detail(detail_url)data = parse_detail(detail_html)logging.info('get detail data %s', data)logging.info('saving data to json file')save_data(data)logging.info('data saved successfully')if __name__ == '__main__':pool = multiprocessing.Pool()pages = range(1, TOTAL_PAGE + 1)pool.map(main, pages)pool.close()pool.join()

第三章 网页数据的解析提取

1 XPath的使用

节点轴选择

2 Beautiful Soup的使用

方法选择器

find_all(name, attrs,text):查询所有符合条件的元素

name:查询节点名称

attrs:查询属性(字典)

text:用来匹配的文本find():返回第一个匹配的元素

第四章 数据的存储

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。