简介
网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。
通过网络爬虫,我们可以爬取到网页的一些有用的信息,一个网页源代码往往很复杂,可以通过正则表达式匹配网页中我们需要的元素,以爬取到相关信息,也可以通过利用bs4库中的一些方法从网页中提取数据。
一个简单的爬虫例子
爬取豆瓣电影 Top 250的电影名字和图片。
为了避免爬取网页时ip被封,可以将requests请求到的网页源代码写入文件,再通过读取文件获取想要的信息。
import requests
url = "https://movie.douban.com/top250"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; '
'Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/92.0.4515.131 '
'Safari/537.36'
}
resp = requests.get(url, headers=headers)
with open('豆瓣电影.html', 'w', encoding='utf-8') as file:
file.write(resp.text)
requests请求方法中有两个参数,第一个是网址,第二个是访问的请求头,请求头获取方法如下:
打开豆瓣电影 Top 250首页的开发者工具,找到导航栏上的Network并点击,然后刷新一下,点击任意行(一般点击第一行)。
找到Request Headers下的User-Agent
打开刚刚写好的文件,使用正则表达式匹配需要的信息。
import re
with open('豆瓣电影.html', 'r', encoding='utf-8') as file:
content = file.read()
# print(content)
re_str = r'<img.*?alt="(.*?)" src="(.+?)".*?>'
pattern = re.compile(re_str)
results = pattern.findall(content)
for alt, src in results:
print(alt)
print(src)
使用search方法匹配一条电影的名字和图片链接的信息。
import re
with open('豆瓣电影.html', 'r', encoding='utf-8') as file:
content = file.read()
# print(content)
re_str = r'<img width="100" alt="(.*)" ' \
r'src="([a-z]{5}.{3}[a-z\d]{4}\.[a-z]{8}\.[a-z]{3}/view/photo/s_ratio_poster/public/p[0-9]{9}.jpg)" class="">'
result = re.search(re_str, content)
# print(result)
# span()输出匹配到的字符串的起始位置和结束位置
print(result.span())
# group(),将分组中的内容返回
# 如果参数是0,将所有分组内容输出
# print(result.group(0))
print(result.group(1))
print(result.group(2))
# groups()将正则表达式中分组的内容合成一个元组
print(result.groups())
运行结果:
(9883, 10002)
肖申克的救赎
https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg
('肖申克的救赎', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg')
bs4语法
bs4
(Beautiful Soup 4)是一个三方库,用之前需要安装,使用pip install beautifulsoup4
命令安装即可,导入时使用import bs4
导入。
import bs4
# bs4:全称:Beautiful Soup 4,可以从HTML或XML钟提取数据,
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = bs4.BeautifulSoup(html, 'lxml')
# print(soup)
# print(type(soup))
# 格式化前端源码
print(soup.prettify())
# 打印标签:只打印第一个某某标签的内容
print(soup.head)
print(soup.head.title)
# 打印标签内容:4钟方法
print(soup.head.title.string)
print(soup.head.title.get_text())
print(soup.head.title.text)
print(soup.head.title.contents)
# 选择标签内容方法
# select:使用(id、class、标签、属性、父子、后代、兄弟、相邻兄弟等选择器)去选择标签内的内容,返回的结果是一个列表
# select_one:使用(id、class、标签、属性、父子、后代、兄弟、相邻兄弟等选择标签内的内容,返回的结果是select结果中的第一个元素
p_list = soup.select('body > p')
print(p_list)
p_list1 = soup.select('body > .title')
print(p_list1)
p = soup.select_one('body > p')
print(p)
使用方法:
- 第一步:使用bs4的接口将网页源代码生成一个
bs4
对象。 - 第二步:通过对象的
select
或select_one
方法来提取数据。
bs4
对象中有两个参数,第一个参数是网页源代码,第二个参数是lxml
解析器。
下面来看一个例子:
爬取成都链家二手房的房源信息,包括标题,链接,位置,单价,总价。
import requests
import bs4
url = 'https://cd.lianjia.com/ershoufang/rs/'
headers = {
'User-Agent': 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
}
resp = requests.get(url, headers=headers)
# print(resp.status_code)
# print(resp.text)
soup = bs4.BeautifulSoup(resp.text, 'lxml')
# print(soup)
li_list = soup.select('body > .content > .leftContent > .sellListContent > .clear')
print(li_list)
print(len(li_list))
for i in li_list:
# print(i)
# print(type(i))
# 标题
# a标签里只有a标签的信息,可以使用select_one.text获取文本信息
title = i.select_one('li > .info > .title > a').text
print(title)
# 超链接
# attrs[属性名]可以获取标签里的对应属性
href = i.select_one('li > .info > .title > a').attrs['href']
print(href)
# 位置
# positionInfo类标签中只有a标签,可以使用select_one.text获取文本信息
area = i.select_one('li > .info > .flood > .positionInfo').text.replace(' ', '')
print(area)
# 单价
unit_price = i.select_one('li > .info > .priceInfo > .unitPrice > span').text
print(unit_price)
# 总价
total_price = i.select_one('li > .info > .priceInfo > .totalPrice').text
print(total_price)
print('*' * 50)
代理IP爬取网页
检测代理IP是否可用
"""
此代码为代理IP可用性检测模块,可准确筛选出尚未失效IP
注:
1.此代码只针对TXT数据格式接口。
2.尚未失效IP不一定为爬虫可用IP
3.使用时,请调用check_ip(url),url为TXT数据格式接口地址
"""
import requests
import telnetlib
import re
from concurrent.futures.thread import ThreadPoolExecutor
# 请求接口,匹配出代理IP,多线程检测
def check_ip(url):
real_ip = []
# 检测代理IP是否失效
def telnet_ip(ip, port):
try:
telnetlib.Telnet(ip, port, timeout=1)
real_ip.append(f'{ip}:{port}')
except:
pass
while True:
try:
resp = requests.get(url)
# print(resp.text)
ip_data = re.findall('(\d+\.\d+\.\d+\.\d+):(\d+)', resp.text)
with ThreadPoolExecutor(max_workers=16) as pool:
for ip, port in ip_data:
pool.submit(telnet_ip, ip, port)
return real_ip
except:
pass
使用可用的代理IP爬取网页
from check_proxies import check_ip
import requests
flag = True
while flag:
URL = 'http://api.66daili.cn/API/GetCommonProxy/?' \
'orderid=2291244402101903832&num=20&token=66daili&format=' \
'text&line_separator=win&protocol=http&anonymous=elite,anonymous,' \
'transparent&area=%E4%B8%AD%E5%9B%BD&proxytype=https&speed=fast#api'
ip_list = check_ip(URL)
# print(ip_list)
for i in range(len(ip_list)):
douban_url = 'https://movie.douban.com/top250'
headers = {
'User-Agent': 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
}
# 代理IP
proxy = {
'http': 'http://' + ip_list[i],
'https': 'https://' + ip_list[i]
}
try:
resp = requests.get(url=douban_url, headers=headers, proxies=proxy, timeout=1)
if resp.status_code == 200:
print(resp.text)
flag = False
break
except:
print('ERROR')
爬虫案例
爬取中国新闻网新闻类型、标题、链接、发布时间,进行多页爬取,并且只爬取当天发布的新闻。
import requests
import bs4
import time
page = 1
flag = True
while flag:
url = f'https://www.chinanews.com/scroll-news/news{page}.html'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
}
resp = requests.get(url, headers=headers)
resp.encoding = 'utf-8'
# print(resp.text)
page += 1
soup = bs4.BeautifulSoup(resp.text, 'lxml')
li_list = soup.select('body > #content > #content_right > .content_list > ul > li')
# print(li_list)
# print(len(li_list))
for i in li_list:
# print(i)
# print(type(i))
try:
localtime = time.localtime()
# 发布时间
date = i.select_one('li > .dd_time').text
if f'{localtime.tm_mon}-{localtime.tm_mday}' == date[:-6]:
# 类型
news_type = i.select_one('li > .dd_lm').text
# 标题
title = i.select_one('li > .dd_bt').text
# 链接
href = i.select_one('li > .dd_bt > a').attrs['href']
print(news_type, title, 'https:' + href, date)
else:
flag = False
break
except Exception as err:
print(err)
爬取到网页源代码后,发现乱码,解决办法是将请求页面的编码方式改为相对应的编码方式,可以通过查看网页源代码,获取网页的编码方式。
由于有的li标签内容为空,可以使用异常处理将错误信息打印出来。