python爬虫由浅入深(基础篇)

admin 2022年10月17日10:24:06python爬虫由浅入深(基础篇)已关闭评论31 views字数 13889阅读46分17秒阅读模式

前言

爬虫是安全学习必不可少的一部分,笔者在这里记录自己的学习历程,希望能分享一下我的心得供大家参考学习

urllib库基本知识

urllib和requets库是python爬虫必不可少的,从这里开始基于的是urllib库,之后会写requests库

http网站的访问

import urllib.request
def load_baidu():
url='http://www.baidu.com'
response=urllib.request.urlopen(url)
print(response.read())
load_baidu()

这里调用的urllib的request模块,通过urlopen函数访问了百度,并通过response函数接收输出

python爬虫由浅入深(基础篇)

输出界面排版很乱我们可以通过i/o输出一下文件

f=open('baidu.html','w')
f.write(response.read().decode('utf-8'))

这里要注意一点response.read()默认为byte类型,这会与windows默认编码冲突,必须加上decode('utf-8')否则会报错

https网站的访问

众所周知https协议比http更加安全,并且加密,通过http的方法我们是无法获取到页面的数据,在这里我们可以利用添加header头部的user-agent表示来绕过,UA标识我们可以通过百度或者抓包来获取自己的UA表示标识

```
import urllib.request
def load_baidu():
url='https://www.baidu.com'
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
}
#创建请求对象
request=urllib.request.Request(url,headers=header)
#进行网络请求获取数据
response=urllib.request.urlopen(request)
print(response.read())
load_baidu()

```

通过字典的形式在创建请求对象时插入header,headers不仅可以再创建时候插入,通过add_header命令同样可以插入

request.add_header('User-agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36')

同样我们不仅可以插入UA标识,我们也可以插入自定义的数据

```
header = {
'haha':'haihaihai',
'hello': 'hello world',
}
````

通过request.get_header("User-agent")可以进行对header头部的数据进行查询

查询时要注意开头首字母大写其余全部小写(我们定义header时无论大小写也会被转换到这种形式),否则返回结果为空

对带入中文参数的url进行访问

import urllib.request
def load_baidu():
url='http://www.baidu.com/s?wd=动物园'
urllib.request.urlopen(url)
load_baidu()

这种url带有中文参数的情况下会报错UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-12: ordinal not in range(128),这是因为python:是解释性语言;解析器只支持 ascii 0 - 127 不支持中文,于是需要进行url编码

```
import urllib.request
import urllib.parse
import string
def load_baidu():
url='https://www.baidu.com/s?wd=动物园'
url=urllib.parse.quote(url,safe=string.printable)
urllib.request.urlopen(url)
load_baidu()

```

通过parse函数将url进行编码,safe变量的含义是不进行编码的函数,string.printable是列出所有可用的字母以及标点符号不编码

对带有多个参数的url进行访问

import urllib.request
import urllib.parse
def get_params():
url = "http://www.baidu.com/s?"
params = {
"wd":"中文",
"key":"zhang",
"value":"san"
}
str_params = urllib.parse.urlencode(params)
print(str_params)
final_url = url + str_params
response = urllib.request.urlopen(final_url)
data = response.read().decode("utf-8")
print(data)
get_params()

通过urlencode函数会自动将字典里的数据拼接为get请求的形式(中文会自动编码)wd=%E4%B8%AD%E6%96%87&key=zhang&value=san

代理的添加

代理服务器的出现会让我们摆脱被封ip的困境

request库有有许多处理器我们用的是ProxyHandler处理器

python爬虫由浅入深(基础篇)

用快代理中的免费代理即可https://free.kuaidaili.com/free/inha/1/

```
import urllib.request
def create_proxy_handler():
url = "http://www.baidu.com"
#添加代理
proxy = {
#免费代理的写法
"http":"106.55.15.244:8889"
}
#代理处理器
proxy_handler = urllib.request.ProxyHandler(proxy)
#创建自己opener
opener = urllib.request.build_opener(proxy_handler)
#拿着代理ip去发送请求
response = opener.open(url)
data = response.read().decode("utf-8")
create_proxy_handler()

```

proxy字典设置代理服务器,随后创建代理处理器,创建opener发送请求即可

小技巧之返回状态码

HTTP状态码表

try:
response = urllib.request.urlopen(url)
except urllib.request.HTTPError as error:
print(error.code)
except urllib.request.URLError as error:
print(error)

可以查看返回的错误

登录网站获取cookie一劳永逸

目标:利用python登录此网站并且带着cookie去访问个人中心

Cookie,有时候也用复数形式Cookies,这种是指某些网站为了辨别用户身份,简单来说就是当您输入一次网站的用户名和密码之后,下一次就可以不需要输入。

目标网站https://www.yaozh.com/

需要用cookie于是我们需要HTTPCookieProcessor处理器,如果需要代理我们ProxyHanlder处理器,不同的有不同的处理器与之对应

利用burp抓取登录请求包

python爬虫由浅入深(基础篇)

我们发现最重要的四个参数为username,pwd,formhash,backurl 所以我们利用访问网站时也要带上这四个参数

```
import urllib.request
from http import cookiejar
from urllib import parse
login_url = 'https://www.yaozh.com/login'

登录的参数

login_form_data = {
"username": "aaaaa",
"pwd": "bbbbb",
"formhash": "A763914773",
"backurl": "https%253A%252F%252Fwww.yaozh.com%252F"
}

发送登录请求POST

cook_jar = cookiejar.CookieJar()

定义有添加 cook 功能的 处理器

cook_hanlder = urllib.request.HTTPCookieProcessor(cook_jar)

根据处理器 生成 opener

opener = urllib.request.build_opener(cook_hanlder)

带着参数 发送post请求

添加请求头

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}

1.参数 将来 需要转译 转码; 2. post请求的 data要求是bytes

login_str = parse.urlencode(login_form_data).encode('utf-8')
login_request = urllib.request.Request(login_url, headers=headers, data=login_str)

如果登录成功, cookjar自动保存cookie

opener.open(login_request)

代码带着cooke去访问 个人中心

center_url = 'https://www.yaozh.com/member/'
center_request = urllib.request.Request(center_url, headers=headers)
response = opener.open(center_url)

bytes -->str

data = response.read().decode('utf-8')
print(data)

```

cookiejar是为了存储cookie,首先利用HTTPCookieProcessor处理器新建一个opener,当网站登录成之后cookiejar会自动存储cookie。因为是https网站添加UA标识,对data数据进行转义创建login_str,创建login_request对象,将header头和post数据存入login_request对象中,再利用HTTPCookieProcessor处理器创建的opener进行访问login_request,登陆成功之后会自动获取cookie,于是可以登录个人中心。


基于Requsts库的基本知识

Request为第三方库需要pip install request下载

request实际用法与urllib并无太大的差异,但是比urllib方便,很多需要注意的地方requests库都会自动帮我们处理

import requests
url = 'https://www.baidu.com/s'
params = {
'wd':"动物园"
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}
response = requests.get(url,headers=headers, params=params)
data = response.content.decode()

同样是访问一个网站,requests使用户少了对中文的编码,以及对参数的拼接,大大提高了便利性

requests使用代理

```
import requests
url = 'http://www.baidu.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36'
}
free_proxy = {'http': '27.17.45.90:43411'}
response = requests.get(url=url, headers=headers, proxies=free_proxy)
print(response.status_code)

```

verify参数

import requests
url = 'https://www.12306.cn/mormhweb/'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36'
}
response = requests.get(url=url, headers=headers, verify=False)
data = response.content.decode()

当我们请求12306或者银行等网站时,就算加了ua标识也会失败,因为他们https用的是自己的证书,于是在get函数里传入verify=False参数,可以告诉浏览器忽略证书继续访问

使用requests对药智网个人中心进行访问

```
import requests

请求数据url

member_url = 'https://www.yaozh.com/member/'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36'
}

session 类 可以自动保存cookies === cookiesJar

session = requests.session()

1.代码登录

login_url = 'https://www.yaozh.com/login'
login_form_data = {
'username':'xiaomaoera12',
'pwd': 'lina081012',
'formhash': '54AC1EE419',
'backurl': 'https%3A%2F%2Fwww.yaozh.com%2F',
}
login_response = session.post(login_url,data=login_form_data,headers=headers)
print(login_response.content.decode())

2.登录成功之后 带着 有效的cookies 访问 请求目标数据

data = session.get(member_url,headers=headers).content.decode()

```

这里的session类 有用会话保持功能,相当于cookiesjar访问成功之后cookie自动保存


爬虫必备之正则匹配

正则表达式是学习爬虫必不可少的一项技能,对提高数据的搜索有很大的帮助

正则表达式全解析+常用示例

常用正则表达式合集

python爬虫由浅入深(基础篇)

python爬虫由浅入深(基础篇)

在python中需要用到自带的re模块

匹配字符串中的中文

`匹配字符串中的中文

import re
two = '\[网页是最新版本的,适配移动端\](https://www.baidu.com/)'
pattern = re.compile('[\u4e00-\u9fa5]+')
result = pattern.findall(two)
print(result)

[\u4e00-\u9fa5]为unicode编码中中文编码的位置

百度新闻抓取新闻并保存

python爬虫由浅入深(基础篇)

通过审查元素对新闻标题的比较我们可以看到,只有mon元素的数值发生变化,target是不改变的,所以我们使用正则匹配字符re.compile('\<a href="(.*?)" mon="(.*?)" target="_blank">(.*?)\</a>')

```
import re
import requests
url = 'http://news.baidu.com/'
headers = {
"User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36'
}
data = requests.get(url, headers=headers).content.decode()
pattern = re.compile('(.*?)')
result = pattern.findall(data)
for i in result:
response=requests.get(i[0])
data=response.content.decode()
print(data)
try:
with open(i[2]+'.html','w',encoding='utf-8') as f:
f.write(data)
f.close()
except:
pass
print(result)

```

result的形式为如下

python爬虫由浅入深(基础篇)

使用for循环遍历result 再用i/o流保存访问i[2]的结果即可

正则匹配不要盲目学,从做中学,盲目的学一堆会忘得很快,多总结常用的方式即可

xpath的简单使用

点我阅读详细使用

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集,简单来说就是寻找标签的位置和获取标签里的内容

如果使用正则表达式感觉太麻烦,某些情况下不妨试试xpath

python需要pip install lxml库

xpath 语法 1. 节点 /
2. 跨节点: //
3. 精确的标签: //a[@属性="属性值"]
4. 标签包裹的内容 text()
5. 属性:@href

python写法

import requests
from lxml import etree
url = 'http://news.baidu.com/'
headers = {
"User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}
data = requests.get(url, headers=headers).content.decode()
xpath_data = etree.HTML(data)
result = xpath_data.xpath('/html/head/title//text()')
result = xpath_data.xpath('//a/text()')
result = xpath_data.xpath('//a[@mon="ct=1&a=2&c=top&pn=18"]/text()')
result = xpath_data.xpath('//a[@mon="ct=1&a=2&c=top&pn=18"]/@href')
result = xpath_data.xpath('//li/a/text()')

entree.HTML(data)实例化一个 etree对象 ,且需要将被解析的页面源码数据加载到该对象中;

调用etree对象中的 xpath方法 ,结合xpath表达式,实现标签的定位,和内容的捕获;

python爬虫由浅入深(基础篇)

如果用/一级一级获取太麻烦,可以用//跳级获取,会得到所有的需要标签,在标签后/text()为获取标签里的内容,@符是定义标签里的内容,获取定义内容标签包含的内容。

xpath模糊查询

例子:

python爬虫由浅入深(基础篇)

//div[contain(@class,"point")] 意思为div标签里class元素只要包含point字符串,这个元素就会被取出来

取平级关系的下一个节点

following-sibling::*

bs4介绍

  1. BeautifulSoup简称: bs4 。
  2. 什么是BeatifulSoup? BeautifulSoup,和lxml一样,是一个html的解析器,主要功能也是解析和提取数据 。
  3. 优缺点?

  4. 缺点:效率没有lxml的效率高

  5. 优点:接口设计人性化,使用方便。

作用:

  • pc端网络中抓取数据如:百度网站、腾讯网站等,随着的网络种类的增多,我们应该去寻找解析这个网站最适合的解析方法
  • 与re和xpath模块的区别
    • re模块:使用起来过于麻烦且阅读性不好,万能,难
    • xpath模块:需要使用一些特定的语法,简单
    • bs4模块:只需要记住一些方法如:find()、find_all(),后面会发现bs4可以认为是re和xpath的混合使用,更简单

bs4库的安装pip install beautifulsoup4

使用:from bs4 import BeautifulSoup

设置bs4解析库:soup = BeautifulSoup(text, 'lxml')

格式化输出补全result = soup.prettify()

bs4的使用

基本用法

获取head标签里全部内容:

```
soup = BeautifulSoup(html_doc, 'lxml')
result = soup.head
````

获取标签里包含的内容(可以是获取注释中的内容,也可以非注释内容):

```
result = soup.p.string
````

获取a标签href属性的内容:

result = soup.a['href']

find以及find_all的使用

find(只返回第一个结果)

查询P标签中的属性和内容:

```
result = soup.find(name="p")
````

查询属性class为title的元素(注意attrs需要用字典形式)

result = soup.find(attrs={"class": "title"})

查询class属性为story的p标签:

result = soup.find(
name='p',
attrs={"class": "story"},
)

find_all

和find差别不大,只是find列出一个数据,find_all列出了所有数据

例子:result = soup.find_all("a", limit=1)[0]

因为是返回多个数据所以是列表形式,取数据时需要用下标,limit属性为1时find=find_all

python爬虫由浅入深(基础篇)

通过查看代码发现find本质也是调用了find_all

css选择器select_one,select

bs4的select()方法接受一个字符串参数,返回一个列表,这个字符串参数就是一个css选择器

选择class是sister的标签:

```
result = soup.select('.sister')
````

查询id是one的标签:

```
result = soup.select('#one')
````

查询head中后代是title的:

result = soup.select('head title')

同时查询标签是title和class为title的:

result = soup.select('title,.title')

查询标签包裹的内容:

result = soup.select('.title')[0].get_text()

查询class为link1的标签的href的属性

result = soup.select('#link1')[0].get('href')

正则表达式,xpath,bs4数据解析都可以用,根据实际情况来定

爬虫实例练习

经过了漫长的学习,我们这里用三个小实例实战一下

爬虫实例1:ChainNode标题,链接爬取

目标网址https://www.chainnode.com/forum/61

源代码:

```
import requests
from lxml import etree
import json
class BtcSpider(object):
def init(self):
self.url='https://www.chainnode.com/forum/61-'
self.header={
'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}
self.data_list=[]
def get_response(self,url):
response=requests.get(url,headers=self.header)
data=response.content.decode()
return data
def parse_data(self,data):
base_url='https://www.chainnode.com'
x_data=etree.HTML(data)
url_list=x_data.xpath('//a[@class="link-dark-major font-bold bbt-block"]/@href')
title_list=x_data.xpath('//a[@class="link-dark-major font-bold bbt-block"]/span/text()')
for i in range(len(url_list)):
news = {}
news['title']=title_list[i]
news['url']=base_url+url_list[i]
self.data_list.append(news)
def save_data(self):
data=json.dumps(self.data_list)
with open('chainnode.json','w',encoding='utf-8') as f:
f.write(data)
f.close()
def run(self):
for i in range(1,6):
url=self.url+str(i)
data=self.get_response(url)
self.parse_data(data)
self.save_data()
BtcSpider().run()

```

思路分析

采用了xpath的方法,没有什么难度

调用了json模块,采用的json的格式存储,将存储的内容放到json解析网站解析即可

python爬虫由浅入深(基础篇)

爬虫实例2:暗月师傅博客文章爬取

目标网站https://www.moonsec.com/

源代码:

import requests
import re
ban_word=[ '\\','/','|',':','*','?','"','<','>']
class moonSpider(object):
def __init__(self):
self.url='https://www.moonsec.com/wp-admin/admin-ajax.php'
self.header={
'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}
self.post_data={
'action' : 'wpcom_load_posts',
'page' : ''
}
def get_response(self):
request=requests.post(self.url,data=self.post_data,headers=self.header,verify=False)
data=request.content.decode()
return data
def parse_data(self,data):
pattern=re.compile('<a class="item-img-inner" href="(.*?)" title="(.*?)" target="_blank">')
data_list=pattern.findall(data)
return data_list
def save_data(self, data_list):
for i in data_list:
request=requests.get(i[0],verify=False)
data=request.content.decode()
for j in ban_word:
if j in i[1]:
i[1].replace('j',"")
try:
with open(f'./moonsec/{i[1]}+.html','w',encoding='utf-8') as f:
f.write(data)
f.close()
except:
pass
def run(self):
for i in range(1,6):
self.post_data['page']=str(i)
data=self.get_response()
data_list=self.parse_data(data)
self.save_data(data_list)
moonSpider().run()

python爬虫由浅入深(基础篇)

思路分析

访问的时候python报了错,发现是证书问题,于是加上verify=False参数即可

测试时,发现暗月师傅的博客是用POST发送数据,通过点击网页的“查看更多”按钮发送数据包。

通过抓包我们看到:page参数控制着要显示的页数,如果我们输入page=3,但是目前我们还在显示第一页的数据,就会跳过第二页只显示第一页和第三页的数据,于是如何抓取每一页完整的数据是一个难点,因为python不会保存上一次for循环访问的数据

python爬虫由浅入深(基础篇)

通过抓包我们发现https://www.moonsec.com/wp-admin/admin-ajax.php。这个接口可以返回任意一页的内容并且不会掺杂其他页,于是成为我们的突破口,通过正则表达式顺理成章的完成了爬虫

爬虫实例3:游戏王卡组下载

目标网址https://www.ygo-sem.cn/kz.aspx

不重要的废话:游戏王这个游戏伴随笔者的童年,这个网站存在很长时间,当时特地找人花了50把卡组全部下载下来,今天笔者就来实现小时候没有实现的愿望

源代码:

import requests
import re
import wget
from lxml import etree
class ygoSpider(object):
def __init__(self):
self.url='https://www.ygo-sem.cn/kz.aspx?te=2022-07-03&tb=2016-03-11&q=&start='
self.header = {
'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}
def response(self,url):
request=requests.get(url,headers=self.header)
data=request.content.decode()
return data
def parse_data(self,data):
x_data=etree.HTML(data)
data_list=x_data.xpath('//button[@title="下载ydk请先登录"][@class="btn btn-primary disabled"]/@onclick')
name_list=x_data.xpath('//h4/text()')
name_list2=[]
for i in range(15):
name_list2.append(name_list[i].strip())
return data_list,name_list2
def run(self):
count=0
base_url='https://www.ygo-sem.cn/'
for i in range(0,12240,15):
url=self.url+str(i)
data=self.response(url)
data_list,name_list=self.parse_data(data)
re_data=re.compile("window.parent.location.href='(.*?)'")
for j in range(15):
a=re_data.findall(data_list[j])
print(a)
b=''.join(a)
down_url=base_url+b
wget.download(down_url,f'./卡组/{name_list[j]}.ydk')
count+=1
print(f'第{count}页已下载完,共下载{i+15}个')
ygoSpider().run()

python爬虫由浅入深(基础篇)

思路分析

python爬虫由浅入深(基础篇)

网站原本是未登录不可以下载,但是通过对网站的分析源代码中会显示下载地址所以无需登录,使用了xpath将onclick元素中的数据提取出来,随后使用re模块用正则表达式将onclick中的数据进一步提取,提取出下载部分,随后和网址进行拼接用wget模块下载,文件的名字是用xpath对h4标签进行了提取。这个案例是xpath 正则的集合,都不难,细心就很容易写出来

总结

本文介绍了urllib,requests,正则表达式,xpath和bs4的使用,最后通过三个案例来练习。大家通过这三个案例也可以看出requests比urllib方便很多,只要写的过程中注意点细节,结构不是很难的网站学完这篇文章做完案例都可以轻松拿捏。

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年10月17日10:24:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   python爬虫由浅入深(基础篇)http://cn-sec.com/archives/1353421.html