Python网络爬虫入门篇!

在这里插入图片描述

  1. 预备知识

学习者需要预先掌握Python的数字类型、字符串类型、分支、循环、函数、列表类型、字典类型、文件和第三方库使用等概念和编程方法。

2. Python爬虫基本流程

a. 发送请求

使用http库向目标站点发起请求,即发送一个Request,Request包含:请求头、请求体等。

Request模块缺陷:不能执行JS 和CSS 代码。

b. 获取响应内容

如果requests的内容存在于目标服务器上,那么服务器会返回请求内容。

Response包含:html、Json字符串、图片,视频等。

c. 解析内容

对用户而言,就是寻找自己需要的信息。对于Python爬虫而言,就是利用正则表达式或者其他库提取目标信息。

解析html数据:正则表达式(RE模块),第三方解析库如Beautifulsoup,pyquery等

解析json数据:json模块

解析二进制数据:以wb的方式写入文件

d. 保存数据

解析得到的数据可以多种形式,如文本,音频,视频保存在本地。

数据库(MySQL,Mongdb、Redis)

文件

3. Requests库入门

Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库。

3.1 Requests库安装和测试

安装:

Win平台:以“管理员身份运行cmd”,执行 pip install requests

测试:

3.2 Requests库的7个主要方法

 方法 说明
 requests.request() 构造一个请求,支撑一下个方法的基础方法。
 requests.get() 获取HTML网页的主要方法,对应HTTP的GET
 requests.head() 获取HTML网页投信息的方法,对应HTTP的HEAD
 requests.post() 向HTML网页提交POST请求的方法,对应HTTP的POST
 requests.put() 向HTML网页提交PUT请求的方法,对应HTTP的PUT
 requests.patch() 向HTML网页提交局部修改请求,对应HTTP的PATCH
 requests.delete() 向HTML网页提交删除请求,对应HTTP的DELETE

带可选参数的请求方式:

requests.request(method,url,**kwargs)

method:请求方式,对应get/put/post等7种

url:获取页面的url链接

**kwargs:控制访问的参数,均为可选项,共以下13个

params:字典或字节系列,作为参数增加到url中

\>>> kv = {'key1':'value1','key2':'value2'}
\>>> r = requests.request('GET','http://python123.io/ws',params=kv)
\>>> print(r.url)  
https://python123.io/ws?key1=value1&key2=value2

data:字典、字节系列或文件对象,作为requests的内容

\>>> kv = {'key1':'value1','key2':'value2'}
>>> r = requests.request('POST','http://python123.io/ws',data=kv)
>>> body = '主题内容'
>>> r = requests.request('POST','http:///python123.io/ws',data=body)

json:JSON格式的数据,作为equests的内容

\>>> kv = {'key1':'value1','key2':'value2'}
>>> r = requests.request('POST','http://python123.io/ws',json=kv)

headers:字典,HTTP定制头

\>>> hd = {'user-agent':'Chrome/10'}
>>> r = requests.request('POST','http://www.baidu.com',headers=hd)

cookies:字典或cookieJar,Request中的cookie

files:字典类型,传输文件

\>>> f = {'file':open('/root/po.sh','rb')}
>>> r = requests.request('POST','http://python123.io/ws',file=f)

timeout:设置超时时间,秒为单位。

\>>> r = requests.request('GET','http://python123.io/ws',timeout=30)

proxies:字典类型,设置访问代理服务器,可以增加登录验证。

\>>> pxs = {'http':'http://user:pass@10.10.10.2:1234',
... 'https':'https://10.10.10.3:1234'}
>>> r = requests.request('GET','http://www.baidu.com',proxies=pxs)

allow_redirects:True/False,默认为True,重定向开关

stream:True/False,默认为True,获取内容立即下载开关

verify:rue/False,默认为True,认证SSL证书开关

Cert:本地SSL证书路径

auth:元组类型,支持HTTP认证功能

3.3 Requests库的get()方法

3.4 Requests的Response对象

Response对象包含服务器返回的所有信息,也包含请求的Request信息

Response对象的属性

3.5 理解Response的编码

注意:编码为ISO-8859-1不支持编译中文,需要设置 r = encoding=“utf-8”

3.6 理解Requests库的异常

Requests库支持常见的6种连接异常

注意:网络连接有风险。异常处理很重要。raise_for_status()如果不等于200则产生异常requests.HTTPError。

3.7 爬取网页的通用代码框架

import requests
def getHTMLText(url):try:r \= requests.get(url,timeout=30)r.raise\_for\_status()r.encoding \= r.apparent\_encodingreturn r.textexcept:return "产生异常"if \_\_name\_\_ == "\_\_main\_\_":url \= "http://www.baidu.com"print(getHTMLText(url))
  1. 网络爬虫的“盗亦有道”:Robots协议

robots是网站跟爬虫间的协议,robots.txt(统一小写)是一种存放于网站根目录下的ASCII编码的文本文件,它通常告诉网络搜索引擎的漫游器(又称网络蜘蛛),此网站中的哪些内容是不应被搜索引擎的漫游器获取的,哪些是可以被漫游器获取的。因为一些系统中的URL是大小写敏感的,所以robots.txt的文件名应统一为小写。robots.txt应放置于网站的根目录下。

网络爬虫的尺寸:

4.1 网络爬虫引发的问题

a. 网络爬虫的“性能”骚扰

web默认接受人类访问,由于网络爬虫的频繁访问会给服务器带来巨大的额资源开销。

b. 网络爬虫的法律风险

服务器上的数据有产权归属,网络爬虫获取数据牟利将带来法律风险

c. 网络爬虫的隐私泄露

网络爬虫可能具备突破简单控制访问的能力,获取被保护的数据从而泄露个人隐私。

4.2 网络爬虫限制

a. 来源审查:判断User-Agent进行限制

检查来访HTTP协议头的user-agent域,只响应浏览器或友好爬虫的访问

b. 发布公告:Robots协议

告知所有爬虫网站的爬取策略,要求遵守Robots协议

4.3 真实的Robots协议案例

京东的Robots协议:

https://www.jd.com/robots.txt

#注释,*代表所有,/代表根目录

4.4 robots协议的遵守方式

对robots协议的理解

自动或人工识别roboes.txt,z再进行内容爬取。

robots协议是建议但非约束性,网络爬虫可以补遵守,但存在法律风险。

原则:人类行为可以补参考robots协议,比如正常阅览网站,或者较少爬取网站频率。

  1. Requests库网络爬虫实战

5.1 京东商品页面爬取

目标页面地址:https://item.jd.com/5089267.html

实例代码:

import requests
url \= 'https://item.jd.com/5089267.html'
try:r \= requests.get(url)r.raise\_for\_status()r.encoding \=r.apparent\_encodingprint(r.text\[:1000\])
except:print("爬取失败")

结果:

5.2 当当网商品页面爬取

目标页面地址:http://product.dangdang.com/26487763.html

代码:

import requests
url \= 'http://product.dangdang.com/26487763.html'
try:r \= requests.get(url)r.raise\_for\_status()r.encoding \=r.apparent\_encodingprint(r.text\[:1000\])
except IOError as e:print(str(e))

出现报错:

HTTPConnectionPool(host=‘127.0.0.1’, port=80): Max retries exceeded with url: /26487763.html (Caused by NewConnectionError(‘<urllib3.connection.HTTPConnection object at 0x10fc390>: Failed to establish a new connection: [Errno 111] Connection refused’,))

报错原因:当当网拒绝不合理的浏览器访问。

查看初识的http请求头:

print(r.request.headers)

代码改进:构造合理的HTTP请求头

import requests
url \= 'http://product.dangdang.com/26487763.html'
try:kv \= {'user-agent':'Mozilla/5.0'}r \= requests.get(url,headers=kv)r.raise\_for\_status()r.encoding \=r.apparent\_encodingprint(r.text\[:1000\])
except IOError as e:print(str(e))

结果正常爬取:

5.3 百度360搜索引擎关键词提交

百度关键词接口:http://www.baidu.com/s?wd=keyword

代码实现:

import requests
keyword \= "python"
try:kv \= {'wd':keyword}r \= requests.get("http://www.baidu.com/s",params=kv)print(r.request.url)r.raise\_for\_status()print(len(r.text))
except IOError as e:print(str(e))

执行结果:

360关键词接口:

http://www.so.com/s?q=keyword

代码实现:

import requests
keyword \= "Linux"
try:kv \= {'q':keyword}r \= requests.get("http://www.so.com/s",params=kv)print(r.request.url)r.raise\_for\_status()print(len(r.text))
except IOError as e:print(str(e))

执行结果:

5.4 网络图片爬取和存储

网络图片链接的格式:

http://FQDN/picture.jpg

校花网:http://www.xiaohuar.com

选择一个图片地址:http://www.xiaohuar.com/d/file/20141116030511162.jpg

实现代码:

import requests
import os
url \= "http://www.xiaohuar.com/d/file/20141116030511162.jpg"
dir \= "D://pics//"
path \= dir + url.split('/')\[-1\] #设置图片保存路径并以原图名名字命名
try:if not os.path.exists(dir):os.mkdir(dir)if not os.path.exists(path):r \= requests.get(url)with open(path,'wb') as f:f.write(r.content)f.close()print("文件保存成功")else:print("文件已存在")
except IOError as e:print(str(e))

查看图片已经存在:

5.5 ip地址归属地查询

ip地址归属地查询网站接口:http://www.ip138.com/ips138.asp?ip=

实现代码:

import requests
url \= "http://www.ip38.com/ip.php?ip="
try:r \= requests.get(url+'104.193.88.77')r.raise\_for\_status()r.encoding \= r.apparent\_encodingprint(r.text)
except IOError as e:print(str(e))

5.5 有道翻译翻译表单提交

打开有道翻译,在开发者模式依次单击“Network”按钮和“XHR”按钮,找到翻译数据:

import requests
import jsondef get\_translate\_date(word=None):url \= "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"#post参数需要放在请求实体里,构建一个新字典form\_data = {'i': word,'from': 'AUTO','to': 'AUTO','smartresult': 'dict','client': 'fanyideskweb','salt': '15569272902260','sign': 'b2781ea3e179798436b2afb674ebd223','ts': '1556927290226','bv': '94d71a52069585850d26a662e1bcef22','doctype': 'json','version': '2.1','keyfrom': 'fanyi.web','action': 'FY\_BY\_REALTlME'}#请求表单数据response = requests.post(url,data=form\_data)#将JSON格式字符串转字典content = json.loads(response.text)#打印翻译后的数据print(content\['translateResult'\]\[0\]\[0\]\['tgt'\])if \_\_name\_\_ == '\_\_main\_\_':word \= input("请输入你要翻译的文字:")get\_translate\_date(word)

执行结果:

6 Beautiful Soup库入门

6.1 简介

Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析“标签树”等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。

Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。

Beautiful Soup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。

6.2 Beautiful Soup安装

目前,Beautiful Soup的最新版本是4.x版本,之前的版本已经停止开发,这里推荐使用pip来安装,安装命令如下:

pip install beautifulsoup4

验证安装:

from bs4 import BeautifulSoup

soup = BeautifulSoup(‘

Hello

’,‘html.parser’)

print(soup.p.string)

执行结果如下:

Hello

注意:这里虽然安装的是beautifulsoup4这个包,但是引入的时候却是bs4,因为这个包源代码本身的库文件名称就是bs4,所以安装完成后,这个库文件就被移入到本机Python3的lib库里,识别到的库文件就叫作bs4。

因此,包本身的名称和我们使用时导入包名称并不一定是一致的。

6.3 BeautifulSoup库解析器

解析器
使用方法
条件
bs4的HTML解析器
BeautifulSoup(mk,'html.parser')
安装bs4库
lxml的HTML解析器
BeautifulSoup(mk,'lxml')
pip install lxml
lxml的XML解析器
BeautifulSoup(mk,'xml')
pip install lxml
html5lib的解析器
BeautifulSoup(mk,'htmlslib')
pip install html5lib

如果使用lxml,在初始化BeautifulSoup时,把第二个参数改为lxml即可:

from bs4 import BeautifulSoup
soup \= BeautifulSoup('<p>Hello</p>','lxml')
print(soup.p.string)

6.4 BeautifulSoup的基本用法

BeautifulSoup类的基本元素

基本元素
说明
Tag
标签,基本信息组织单元,分别用<>和</>标明开头和结尾
Name
标签的名字,<p></p>的名字是‘p’,格式:<tag>.name
Attributes
标签的属性,字典形式组织,格式:<tag>.attrs
NavigableString
标签内非属性字符串,<>...<>中字符串,格式:<tag>.string
Comment
标签内字符串的注释部分,一种特殊的Comment类型

实例展示BeautifulSoup的基本用法:

\>>> from bs4 import BeautifulSoup
\>>> import requests
\>>> r = requests.get("http://python123.io/ws/demo.html")
\>>> demo = r.text
\>>> demo
'<html><head><title>This is a python demo page</title></head>\\r\\n<body>\\r\\n<p class="title"><b>The demo python introduces several python courses.</b></p>\\r\\n<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\\r\\n<a href="http://www.icourse163.org/course/BIT-268001" class="py1" id="link1">Basic Python</a> and <a href="http://www.icourse163.org/course/BIT-1001870001" class="py2" id="link2">Advanced Python</a>.</p>\\r\\n</body></html>'
>>> soup = BeautifulSoup(demo,"html.parser")
\>>> soup.title #获取标题
<title>This is a python demo page</title>
>>> soup.a #获取a标签
<a class\="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1"\>Basic Python</a>
>>> soup.title.string
'This is a python demo page'
>>> soup.prettify() #输出html标准格式内容
'<html>\\n <head>\\n <title>\\n This is a python demo page\\n </title>\\n </head>\\n <body>\\n <p class="title">\\n <b>\\n The demo python introduces several python courses.\\n </b>\\n </p>\\n <p class="course">\\n Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\\n <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">\\n Basic Python\\n </a>\\n and\\n <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">\\n Advanced Python\\n </a>\\n .\\n </p>\\n </body>\\n</html>'
>>> soup.a.name #每个<tag>都有自己的名字,通过<tag>.name获取
'a'
>>> soup.p.name
'p'
>>> tag = soup.a
\>>> tag.attrs
{'href': 'http://www.icourse163.org/course/BIT-268001', 'class': \['py1'\], 'id': 'link1'}
\>>> tag.attrs\['class'\]
\['py1'\]
\>>> tag.attrs\['href'\]
'http://www.icourse163.org/course/BIT-268001'
>>> type(tag.attrs)
<class 'dict'\>
>>> type(tag)
<class 'bs4.element.Tag'\>
>>>

6.5 标签树的遍历

标签树的下行遍历

标签树的上行遍历:遍历所有先辈节点,包括soup本身

标签树的平行遍历:同一个父节点的各节点间

实例演示:

from bs4 import BeautifulSoup
import requests
demo \= requests.get("http://python123.io/ws/demo.html").text
soup \= BeautifulSoup(demo,"html.parser")
#标签树的上行遍历
print("遍历儿子节点:\\n")
for child in soup.body.children:print(child)print("遍历子孙节点:\\n")
for child1 in soup.body.descendants:print(child1)print(soup.title.parent)
print(soup.html.parent)
for parent in soup.a.parents:if parent is None:print(parent)else:print(parent.name)
#标签树的平行遍历
print(soup.a.next\_sibling)
print(soup.a.next\_sibling.next\_sibling)
print(soup.a.previous\_sibling)

7 正则表达式

正则表达式是处理字符串的强大工具,它有自己特定的语法结构,实现字符串的检索、替换、匹配验证都可以。对于爬虫来说,

从HTML里提取想要的信息非常方便。python的re库提供了整个正则表达式的实现

7.1 案例引入

这里介绍一个正则表达式测试工具http://tool.oschina.net/regex,输入待匹配的文本,然选择常用的正则表达式,得到相应的匹配结果,

适合新手入门。这里输入:

hello,my phone is 18898566588 and email is david@gmail.com, and wen is https://www.cnblogs.com/wenwei-blog/

点击“匹配Email地址”,即可匹配出网址。

7.2 常用正则表达式匹配规则

‘.’ 匹配所有字符串,除\n以外

‘-’ 表示范围[0-9]

‘*’ 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。

‘+’ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+

‘^’ 匹配字符串开头

‘$’ 匹配字符串结尾 re

‘\’ 转义字符, 使后一个字符改变原来的意思,如果字符串中有字符*需要匹配,可以\*或者字符集[*] re.findall(r’3\*',‘3*ds’)结[‘3*’]

‘*’ 匹配前面的字符0次或多次 re.findall(“ab*”,“cabc3abcbbac”)结果:[‘ab’, ‘ab’, ‘a’]

‘?’ 匹配前一个字符串0次或1次 re.findall(‘ab?’,‘abcabcabcadf’)结果[‘ab’, ‘ab’, ‘ab’, ‘a’]

‘{m}’ 匹配前一个字符m次 re.findall(‘cb{1}’,‘bchbchcbfbcbb’)结果[‘cb’, ‘cb’]

‘{n,m}’ 匹配前一个字符n到m次 re.findall(‘cb{2,3}’,‘bchbchcbfbcbb’)结果[‘cbb’]

‘\d’ 匹配数字,等于[0-9] re.findall(‘\d’,‘电话:10086’)结果[‘1’, ‘0’, ‘0’, ‘8’, ‘6’]

‘\D’ 匹配非数字,等于[^0-9] re.findall(‘\D’,‘电话:10086’)结果[‘电’, ‘话’, ‘:’]

‘\w’ 匹配字母和数字,等于[A-Za-z0-9] re.findall(‘\w’,‘alex123,./;;;’)结果[‘a’, ‘l’, ‘e’, ‘x’, ‘1’, ‘2’, ‘3’]

‘\W’ 匹配非英文字母和数字,等于[^A-Za-z0-9] re.findall(‘\W’,‘alex123,./;;;’)结果[‘,’, ‘.’, ‘/’, ‘;’, ‘;’, ‘;’]

‘\s’ 匹配空白字符 re.findall(‘\s’,‘3*ds \t\n’)结果[’ ', ‘\t’, ‘\n’]

‘\S’ 匹配非空白字符 re.findall(‘\s’,‘3*ds \t\n’)结果[‘3’, ‘*’, ‘d’, ‘s’]

‘\A’ 匹配字符串开头

‘\Z’ 匹配字符串结尾

\t 匹配衣蛾制表符

‘\b’ 匹配单词的词首和词尾,单词被定义为一个字母数字序列,因此词尾是用空白符或非字母数字符来表示的

‘\B’ 与\b相反,只在当前位置不在单词边界时匹配

‘(?P…)’ 分组,除了原有编号外在指定一个额外的别名 re.search(“(?P[0-9]{4})(?P[0-9]{2})(?P[0-9]{8})”,“371481199306143242”).groupdict(“city”) 结果{‘province’: ‘3714’, ‘city’: ‘81’, ‘birthday’: ‘19930614’}

[] 是定义匹配的字符范围。比如 [a-zA-Z0-9] 表示相应位置的字符要匹配英文字符和数字。[\s*]表示空格或者*号。

常用的re函数:

[^…] 不在[]中的字符,比如[^abc]匹配除了a、b、c之外的字符。

.* 具有贪婪的性质,首先匹配到不能匹配为止,根据后面的正则表达式,会进行回溯。
.*? 满足条件的情况只匹配一次,即懒惰匹配。

7.3 常用匹配方法属性函数

方法/属性
作用
re.match(pattern, string, flags=0)
从字符串的起始位置匹配,如果起始位置匹配不成功的话,match()就返回none
re.search(pattern, string, flags=0)
扫描整个字符串并返回第一个成功的匹配
re.findall(pattern, string, flags=0)
找到RE匹配的所有字符串,并把他们作为一个列表返回
re.finditer(pattern, string, flags=0)
找到RE匹配的所有字符串,并把他们作为一个迭代器返回
re.sub(pattern, repl, string, count=0, flags=0)
替换匹配到的字符串

函数参数说明:

pattern:匹配的正则表达式

string:要匹配的字符串

flags:标记为,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

repl:替换的字符串,也可作为一个函数

count:模式匹配后替换的最大次数,默认0表示替换所有匹配

例子1:

#!/usr/bin/python3
import re
#替换
phone = '18898537584 #这是我的电话号码'
print('我的电话号码:',re.sub('#.\*','',phone)) #去掉注释
print(re.sub('\\D','',phone))
#search
ip\_addr = re.search('(\\d{3}\\.){1,3}\\d{1,3}\\.\\d{1,3}',os.popen('ifconfig').read())
print(ip\_addr)
#match
>>> a = re.match('\\d+','2ewrer666dad3123df45')
\>>> print(a.group())
2 

获取匹配的函数:

方法/属性
作用
group(num=0)
匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
groups()
返回包含所有小组字符串的元组,从1到所含的小组
groupdict()
返回以有别名的组的别名为键、以该组截获的子串为值的字典
start()
返回匹配开始的位置
end()
返回匹配结束的位置
span()
返回一个元组包含匹配(开始,结束)的位置

re模块中分组的作用?

(1)判断是否匹配(2)灵活提取匹配到各个分组的值。

\>>> import re
\>>> print(re.search(r'(\\d+)-(\[a-z\])','34324-dfsdfs777-hhh').group(0)) #返回整体
34324-d
\>>> print(re.search(r'(\\d+)-(\[a-z\])','34324-dfsdfs777-hhh').group(1))  #返回第一组
34324
>>> print(re.search(r'(\\d+)-(\[a-z\])','34324-dfsdfs777-hhh').group(2))  #获取第二组
d
\>>> print(re.search(r'(\\d+)-(\[a-z\])','34324-dfsdfs777-hhh').group(3))  #不存在。报错“no such group”
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: no such group

7.4 re.compile 函数

compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象。语法格式:
re.compile(pattern[, flags])
参数:
pattern : 一个字符串形式的正则表达式
flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:
re.I 忽略大小写
re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
re.M 多行模式
re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
re.X 为了增加可读性,忽略空格和 # 后面的注释
常用的是re.I和re.S

\>>> import re
\>>> pattern = re.compile('\\d+',re.S)  #用于匹配至少一个数字
>>> res = re.findall(pattern,"my phone is 18898566588")
\>>> print(res)
\['18898566588'\]

7.5 爬取猫眼电影TOP排行

利用requests库和正则表达式来抓取猫眼电影TOP100的相关内容。requests比urllib使用更加方便。

抓取目标

提取猫眼电影TOP的电影名称、时间、评分 、图片等信息。提取的站点URL为https://maoyan.com/board/4

提取结果已文件形式保存下来。

URL提取分析

打开站点https://maoyan.com/board/4,直接点击第二页和第三页,观察URL的内容产生的变化。

第二页:https://maoyan.com/board/4?offset=10

第三页:https://maoyan.com/board/4?offset=20

总结出规律,唯一变化的是offset=x,如果想获取top100电影,只需分开请求10次,offset参数分别设置为0、10、20…90即可。

源码分析和正则提取

打开网页按F12查看页面源码,可以看到,一部电影信息对应的源代码是一个dd节点,首先需要提取排名信息,排名信息在class为board-index的i节点内,这里使用懒惰匹配提取i节点内的信息,正则表达式为:

.\*?board-index.\*?>(.\*?)

随后提取电影图片,可以看到后面有a节点,其内部有两个img节点,经过检查后发现,第二个img节点的data-src属性是图片的链接。这里提取第二个img节点的data-src属性,正则表达式改写如下:

.\*?board-index.\*?>(.\*?).\*?data-src="(.\*?)".\*?name.\*?a.\*?>(.\*?)

再提取主演、发布时间、评分等内容时,都是同样的原理。最后,正则表达式写为:

.\*?board-index.\*?>(.\*?).\*?data-src="(.\*?)".\*?name.\*?a.\*?>(.\*?).\*?star.\*?>(.\*?).\*?releasetime.\*?>(.\*?).\*?integer.\*?>(.\*?).\*?fraction.\*?>(.\*?).\*?

注意:这里不要在Element选项卡中直接查看源码,因为那里的源码可能经过JavaScript操作而与原始请求不通,而是需要从NetWork选项卡部分查看原始请求得到的源码。

代码整合

import json
import requests
from requests.exceptions import RequestException #引入异常
import re
import time
def get\_one\_page(url):try:response \= requests.get(url)if response.status\_code == 200: #由状态码判断返回结果return response.text #返回网页内容return Noneexcept RequestException:return Nonedef parse\_one\_page(html):pattern \= re.compile('<dd>.\*?board-index.\*?>(\\d+)</i>.\*?data-src="(.\*?)".\*?name"><a'+ '.\*?>(.\*?)</a>.\*?star">(.\*?)</p>.\*?releasetime">(.\*?)</p>'+ '.\*?integer">(.\*?)</i>.\*?fraction">(.\*?)</i>.\*?</dd>', re.S) #compile函数表示一个方法对象,re.s匹配多行items = re.findall(pattern, html) #以列表形式返回全部能匹配的字符串。for item in items:  #将结果以字典形式返回键值对yield {   #把这个方法变成一个生成器'index': item\[0\],'image': item\[1\],'title': item\[2\],'actor': item\[3\].strip()\[3:\],'time': item\[4\].strip()\[5:\],'score': item\[5\] + item\[6\]  #将评分整数和小数结合起来}def write\_to\_file(content):with open('result.txt', 'a', encoding='utf-8') as f:  #将结果写入文件f.write(json.dumps(content, ensure\_ascii=False) + '\\n')def main(offset):url \= 'http://maoyan.com/board/4?offset=' + str(offset)html \= get\_one\_page(url)for item in parse\_one\_page(html):print(item)write\_to\_file(item)if \_\_name\_\_ == '\_\_main\_\_':for i in range(10):main(offset\=i \* 10)time.sleep(1)

8 Scrapy框架

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中。
其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。Scrapy 使用了 Twisted异步网络库来处理网络通讯。整体架构大致如下

Scrapy主要包括了以下组件:

  • 引擎(Scrapy)
    用来处理整个系统的数据流处理, 触发事务(框架核心)
  • 调度器(Scheduler)
    用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
  • 下载器(Downloader)
    用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
  • 爬虫(Spiders)
    爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
  • 项目管道(Pipeline)
    负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
  • 下载器中间件(Downloader Middlewares)
    位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
  • 爬虫中间件(Spider Middlewares)
    介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
  • 调度中间件(Scheduler Middewares)
    介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

Scrapy运行流程大概如下:

  1. 引擎从调度器中取出一个链接(URL)用于接下来的抓取
  2. 引擎把URL封装成一个请求(Request)传给下载器
  3. 下载器把资源下载下来,并封装成应答包(Response)
  4. 爬虫解析Response
  5. 解析出实体(Item),则交给实体管道进行进一步的处理
  6. 解析出的是链接(URL),则把URL交给调度器等待抓取

scrapy常用命令

scrapy startproject <爬虫名称> 创建爬虫名称(唯一)

scrapy genspider<爬虫项目名称> 创建爬虫项目名称

scrapy list 列出所有爬虫名称

scrapy crawl <爬虫名称> 运行爬虫

8.1 scrapy爬虫项目一:爬取豆瓣电影TOP250

爬取目标:电影排名、电影名称、电影评分、电影评论数

创建爬虫项目和爬虫

scrapy startproject DoubanMovieTop

cd DoubanMovieTop

scrapy genspider douban

修改默认“user-agent”和reboots为True

修改settings.py文件以下参数:

USER\_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'
ROBOTSTXT\_OBEY = False

Item使用简单的class定义语法以及Field对象来声明。

写入下列代码声明Item

import scrapy
class DoubanmovietopItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()#排名ranking = scrapy.Field()#电影名称movie\_name = scrapy.Field()#评分score = scrapy.Field()#评论人数score\_num = scrapy.Field()

分析网页源码抓取所需信息

# -\*- coding: utf-8 -\*-
import scrapy
from DoubanMovieTop.items import DoubanmovietopItemclass DoubanSpider(scrapy.Spider):name \= 'douban'#allowed\_domains = \['movie.douban.com'\]def start\_requests(self):start\_urls \= 'https://movie.douban.com/top250'yield scrapy.Request(start\_urls)def parse(self, response):item \= DoubanmovietopItem()movies \= response.xpath('//ol\[@class="grid\_view"\]/li')for movie in movies:item\['ranking'\] = movie.xpath('.//div\[@class="pic"\]/em/text()').extract()\[0\]item\['movie\_name'\] = movie.xpath('.//div\[@class="hd"\]/a/span\[1\]/text()').extract()\[0\]item\['score'\] = movie.xpath('.//div\[@class="star"\]/span\[@class="rating\_num"\]/text()').extract()\[0\]item\['score\_num'\] = movie.xpath('.//div\[@class="star"\]/span/text()').re(r'(\\d+)人评价')\[0\]  #Selector也有一种.re()yield itemnext\_url \= response.xpath('//span\[@class="next"\]/a/@href').extract()if next\_url:next\_url \= 'https://movie.douban.com/top250' + next\_url\[0\]yield scrapy.Request(next\_url)

运行爬虫写入文件中

scrapy crawl douban -o douban.csv

使用wps打开excel表格查看抓取结果

如果你是准备学习Python或者正在学习(想通过Python兼职),下面这些你应该能用得上: 包括:Python安装包、Python web开发,Python爬虫,Python数据分析,人工智能、自动化办公等学习教程。带你从零基础系统性的学好Python!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/10400.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】踏上C++学习之旅(五):auto、范围for以及nullptr的精彩时刻(C++11)

文章目录 前言1. auto关键字&#xff08;C11&#xff09;1.1 为什么要有auto关键字1.2 auto关键字的使用方式1.3 auto的使用细则1.4 auto不能推导的场景 2. 基于范围的for循环&#xff08;C11&#xff09;2.1 范围for的语法2.2 范围for的使用条件 3. 指针空值nullptr&#xff0…

科研绘图系列:R语言组合多个不同图形(violin density barplot heatmap)

文章目录 介绍加载R包数据下载函数图1: Boxplots导入数据数据预处理画图图2: Violin导入数据数据预处理画图图3: Density plots per habitat数据预处理画图图4: Density plots per depth数据预处理画图图5: bar plot准备颜色导入数据数据预处理数据预处理画图图6: Mantel Heat…

系统聚类的分类数确定——聚合系数法

breast_cancer数据集分析——乳腺癌诊断 #读取乳腺癌数据 import pandas as pd import numpy as np from sklearn.datasets import load_breast_cancer data load_breast_cancer() X data.data y data.target.. _breast_cancer_dataset:Breast cancer wisconsin (diagnosti…

jsp+sevlet+mysql实现用户登陆和增删改查功能

jspsevletmysql实现用户登陆和增删改查功能 一、系统介绍二、功能展示1.用户登陆2.用户列表3.查询用户信息4.添加用户信息5.修改用户信息6.删除用户信息 四、其它1.其他系统实现 一、系统介绍 系统主要功能&#xff1a; 用户登陆、添加用户、查询用户、修改用户、删除用户 二…

一文了解Java序列化

Java 序列化&#xff08;Serialization&#xff09;是将对象的状态转换为字节流&#xff0c;以便将对象的状态保存到文件中或通过网络传输的过程。反序列化&#xff08;Deserialization&#xff09;则是将字节流恢复为原始对象。Java 序列化主要通过 Serializable 接口实现。 为…

斗破QT编程入门系列之前言:认识Qt:获取与安装(四星斗师)

本系列是在学习完C之后&#xff0c;然后通过Qt构建界面来&#xff0c;赋予枯燥的代码新的样貌&#xff0c;这样我们才能开发出更人性化的程序&#xff0c;同时会进一步提高初学者对编程的兴趣&#xff0c;大家加油&#xff0c;斗破Qt来了。 斗破Qt目录&#xff1a; 斗破Qt编程…

Spring Boot - 扩展点 EnvironmentPostProcessor源码分析及真实案例

文章目录 概述EnvironmentPostProcessor 作用EnvironmentPostProcessor 实现和注册创建类并实现接口注册到 Spring Boot常见应用场景 源码分析1. EnvironmentPostProcessor 接口定义2. 扩展点加载流程3. 加载 EnvironmentPostProcessor 实现类4. EnvironmentPostProcessor 执行…

封装的数字滚动组件的实现代码

效果&#xff1a; 学习啦&#xff1a; Vue 是一个渐进式框架&#xff0c;鼓励通过组件化来构建应用&#xff0c;其组件化优势&#xff1a; 代码复用&#xff1a;不同的视图和功能被封装成独立的组件&#xff0c;便于复用。易于维护&#xff1a;每个组件职责单一、耦合度低&…

Unity跨平台基本原理

目录 前言 ​编辑 Mono Unity和Mono的关系 Unity跨平台必备概念 Mono利用 Mono主要构成部分 基于Mono跨平台的优缺点 IL2CPP Mono和IL2CPP的区别 Mono IL2CPP Mono和IL2CPP的使用建议 安装IL2CPP IL2CPP打包存在的问题 类型裁剪 泛型问题 前言 Unity跨平台的基…

计算机网络(3)

UDP是面向无连接的通信协议&#xff0c;UDP数据包括目的端口号和源端口号信息&#xff0c;由于 不需要连接&#xff0c;所以可以实现广播发送&#xff1b; 传输控制层 UDP协议&#xff08;用户数据报协议&#xff09; UDP通信时不需要接收方确认&#xff0c;属于不可靠的传输&a…

2024年11月8日上海帆软用户大会

2024年11月8日上海帆软用户大会 2024年11月8日&#xff0c;上海成功举办了帆软用户大会&#xff0c;主题为“数字聚力&#xff0c;绽放新机”。大会汇聚了众多行业专家和企业代表&#xff0c;共同探讨数字化转型和商业智能领域的最新趋势和实践。 大会亮点&#xff1a; 专家…

PySimpleGUI和Pymysql

PySimpleGUI 库 PySimpleGUI 是一个用于简化 GUI 编程的 Python 包&#xff0c;它封装了多种底层 GUI 框架&#xff08;如 tkinter、Qt、WxPython 等&#xff09;&#xff0c;提供了简单易用的 API。PySimpleGUI 包含了大量的控件&#xff08;也称为小部件或组件&#xff09;&…

Qt Event事件系统小探1

目录 Qt Event System From qt.doc 如何传递事件 事件类型 事件处理程序 事件过滤器 发送事件 事件的产生和派发 处理我们的事件 来一段好玩的代码 扩展&#xff1a;QWidget如何处理我们的事件&#xff1f; 扩展2&#xff1a;实现一个变色的Label Qt Event System Fr…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于凸多面体仿射变换的用户侧灵活性资源多元聚合方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

vue3组合式API下封装hooks使用生命周期,在await之后调用hooks会有警告

起因&#xff1a;想封装一个hooks实现echarts图表随屏幕大小resize并且组件销毁时移除监听。结果在组件里面调用这个hooks&#xff0c;有个告警提示 [Vue warn]: onBeforeUnmount is called when there is no active component instance to be associated with. Lifecycle inje…

使用Python实现图像的手绘风格效果

使用Python实现图像的手绘风格效果 一、引言二、代码详细解释与示例三、完整框架流程四、运行五、结论附&#xff1a;完整代码 一、引言 在数字图像处理领域&#xff0c;模拟手绘风格是一项有趣且具有挑战性的任务。手绘风格图像通常具有独特的纹理和深浅变化&#xff0c;给人…

window中借助nginx配置vite+vue项目的反向代理步骤

在官网下载好nginx的安装包后&#xff0c;解压后 CMD打开 start nginx 是启动命令 nginx -s stop 停止服务 nginx -s reload 如果重写了nginx.conf文件&#xff0c;要执行这条命令 正常情况下 成功启动和成功停止服务长这样 错误情况&解决 如果nginx -s stop失败 ngi…

花指令例子

如图所示&#xff1a; 指令EB FF的汇编代码为jmp -1&#xff0c;CPU执行到地址处0x6c80c0的指令EB FF时(jmp -1)&#xff0c;EIP为6c80c2, 执行后&#xff0c;EIP为0x6c80c1。但是反汇编器无法自动识别该指令。

关于我的编程语言——C/C++——第八篇

&#xff08;叠甲&#xff1a;如有侵权请联系&#xff0c;内容都是自己学习的总结&#xff0c;一定不全面&#xff0c;仅当互相交流&#xff08;轻点骂&#xff09;我也只是站在巨人肩膀上的一个小卡拉米&#xff0c;已老实&#xff0c;求放过&#xff09; 什么是C C语言是结…

博客园美化

1、主题介绍 使用的 SimpleMemory 这款主题 github官网 2、设置主题并申请 js 代码权限 3、主题设置 博客侧边栏公告 <script type"text/javascript">window.cnblogsConfig {info: {blogIcon: https://ts1.cn.mm.bing.net/th/id/R-C.85775e482741cb7ab7f…