网络爬虫是指一种程序自动获取网页信息的方式,它能够自动化地获取互联网上的数据。通过使用网络爬虫,我们可以方便地获取到网络上的各种数据,例如网页链接、文本、图片、音频、视频等等。
网页是由HTML标签和内容组成,HTML标签通过标签属性可以定位到需要的内容。网页中的样式由CSS控制,JavaScript可以实现网页动态效果。
HTML标签是一种用于构建Web页面的标记语言,它描述了页面的结构和元素。HTML标签通常包含一个起始标签和一个结束标签,例如
。HTML标签也可以包含属性,属性用于提供有关元素的额外信息。例如,元素的
href
属性指定了链接目标的URL地址,而元素的
src
属性指定了要显示的图像文件的URL地址。
CSS是一种用于控制Web页面样式的样式表语言,它可以为HTML元素提供样式和布局。通过CSS,我们可以控制文本的字体、颜色、大小和样式,以及元素的大小、位置、边框和背景等。
JavaScript是用于实现Web页面动态效果的一种编程语言,它可以实现网页上的各种交互效果,例如弹出窗口、表单验证、动画效果等。
Requests是Python中的HTTP库,提供了简洁易用的接口进行HTTP请求。其中,GET请求常用于获取静态网页信息。
我们通过requests.get()方法来发送一个GET请求.
import requests
url = 'https://www.baidu.com'
response = requests.get(url)
print(response.text)
第一行导入了requests模块,第二行指定了要请求的URL地址,在本例中我们使用百度首页作为示例。第三行使用requests库的get()方法来获取该URL的响应对象。响应对象包含了服务器返回的所有信息,包括Header(头部)和Body(主体)两部分。其中Header包含了很多信息,如日期、内容类型、服务器版本等,而Body包含了页面HTML源代码等具体信息。
第四行使用print()函数打印出响应内容的文本形式。运行这段代码,我们就可以在终端中看到百度首页的HTML源代码。
在实际爬虫中,我们可以利用requests模块的一些属性或者方法来解析响应内容,提取需要的数据。例如,可以使用response.status_code属性来获取HTTP状态码,使用response.headers属性来获取HTTP头部信息等。此外,我们还可以使用response.json()方法来解析JSON格式的响应内容,使用response.content方法来获取字节形式的响应内容等。
POST请求与GET请求的区别在于,POST请求会将请求参数放在请求体中,而GET请求则将请求参数放在URL中。通常情况下,POST请求比GET请求更安全,因为它可以隐藏请求参数。
我们通过requests.post()方法来发送一个POST请求,下面是详细的代码分析:
import requests
url = 'http://xxxx.org/post' # 这里使用xxxx.org来演示POST请求
data = {'key1': 'value1', 'key2': 'value2'}
response = requests.post(url, data=data)
print(response.text)
第一行导入了requests模块,
第二行指定了要请求的URL地址 。
第三行定义了请求参数data,这个字典中包含了两个键值对,分别表示key1和key2这两个参数的值。第四行使用requests库的post()方法来发送POST请求并获取响应对象。
我们通过data参数将请求参数放在请求体中,这里使用了字典类型作为请求参数。第五行使用print()函数打印出响应内容的文本形式。运行这段代码,我们就可以在终端中看到xxxx.org返回的响应内容,其中包括了我们发送的请求参数。
在实际爬虫中,我们可以利用requests模块的一些属性或者方法来解析响应内容,提取需要的数据。
import requests
import json
url = 'http://xxxx.org/post' # 这里使用xxxx.org来演示POST请求
data = {'key1': 'value1', 'key2': 'value2'}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(data), headers=headers)
print(response.text)
在这个案例中,我们将请求参数data转换成JSON格式,并使用headers来指定Content-Type为application/json。然后,我们通过requests库的post()方法来发送POST请求。
import requests
proxy = {
'http': 'http://10.10.1.10:3128',
'https': 'https://10.10.1.10:1080'
}
url = 'http://xxxx.org/post' # 这里使用xxxx.org来演示POST请求
data = {'key1': 'value1', 'key2': 'value2'}
response = requests.post(url, data=data, proxies=proxy)
print(response.text)
在这个案例中,我们通过proxy字典来指定代理服务器的地址和端口号。然后,我们通过requests库的post()方法来发送POST请求。
import requests
url = 'http://xxxx.org/post' # 这里使用xxxx.org来演示POST请求
files = {'file': open('myfile.txt', 'rb')}
response = requests.post(url, files=files)
print(response.text)
在这个案例中,我们通过files参数来指定要上传的文件。
open()函数打开文件,第一个参数是文件名,第二个参数是打开方式(rb表示二进制只读模式)。然后,我们通过requests库的post()方法来发送POST请求。
XPath是一种用于选择XML文档中某些部分的语言。在Python中,我们可以使用lxml库来解析XML文档并使用XPath进行选择。
XPath语法主要由路径表达式和基本表达式构成。其中,路径表达式用于选择节点或者节点集合,而基本表达式用于指定某个元素、属性或者其他内容。
表达式 | 描述 |
---|---|
nodename | 选择所有名为nodename的元素 |
/ | 从当前节点选取根节点 |
// | 从当前节点选取任意节点 |
. | 选择当前节点 |
… | 选择当前节点的父节点 |
@ | 选择属性 |
* | 匹配任何元素节点 |
[@attrib] | 选择具有给定属性的所有元素 |
[@attrib=‘value’] | 选择具有给定属性值的所有元素 |
tagname[text() = ‘text’] | 选择具有给定文本的所有tagname元素 |
from lxml import etree
import requests
url = 'https://www.baidu.com'
html = requests.get(url).text
selector = etree.HTML(html)
result = selector.xpath('//title/text()')
print(result[0])
案例中,我们首先发送了一个GET请求获取百度首页的HTML源代码。然后,我们使用lxml库中的etree模块来构建一个XPath解析器,并将HTML源代码传给它进行解析。接着,我们使用XPath表达式’//title/text()'来选择HTML文档中title标签的内容。最后,我们打印出XPath语句返回的结果。
from lxml import etree
xml = '''
Everyday Italian
Giammarco Tomaselli
2010
30.00
Harry Potter
J.K. Rowling
2005
29.99
'''
selector = etree.XML(xml)
result = selector.xpath('//book[1]/title/text()')
print(result[0])
案例中,我们定义了一个XML字符串,并使用etree.XML()方法来创建一个XPath解析器。然后,我们使用XPath表达式’//book[1]/title/text()'来选择XML文档中第一个book元素的title元素的内容。最后,我们打印出XPath语句返回的结果。
from lxml import etree
xml = '''
Everyday Italian
Giammarco Tomaselli
2010
30.00
Harry Potter
J.K. Rowling
2005
29.99
'''
selector = etree.XML(xml)
ns = {'ns': 'http://www.example.com'}
result = selector.xpath('//ns:book[1]/ns:title/text()', namespaces=ns)
print(result[0])
案例中,我们定义了一个带有命名空间的XML字符串,并使用etree.XML()方法来创建一个XPath解析器。然后,我们通过传递一个namespaces参数来指定命名空间的前缀和URI。最后,我们使用XPath表达式’//ns:book[1]/ns:title/text()'来选择第一个book元素的title元素的内容。最后,我们打印出XPath语句返回的结果。
BeautifulSoup是常用的Python第三方库,它提供了解析HTML和XML文档的函数和工具。使用BeautifulSoup可以方便地遍历和搜索文档树中的节点,获取节点属性和文本内容等信息
首先我们需要导入BeautifulSoup模块:
from bs4 import BeautifulSoup
使用BeautifulSoup对HTML文档进行解析,可以通过以下两种方式:
(1) 传递一个HTML字符串作为参数:
html_doc = """
这是标题
第一段落
第二段落
"""
soup = BeautifulSoup(html_doc, 'html.parser')
(2) 传递一个文件路径或文件对象作为参数:
with open('example.html', 'r') as f:
soup = BeautifulSoup(f, 'html.parser')
很多时候,我们需要遍历整个文档树来查找特定的节点,或者获取节点的属性和文本内容。BeautifulSoup提供了多种遍历文档树的方法,包括:
(1) .contents:返回一个包含所有子节点的列表。
for child in soup.body.contents:
print(child)
(2) .children:返回一个包含所有子节点的迭代器。
for child in soup.body.children:
print(child)
(3) .descendants:返回一个包含文档树中所有子孙节点的迭代器。
for element in soup.descendants:
print(element)
(4) .parent:返回一个节点的父节点。
p = soup.body.p
print(p.parent)
(5) .parents:返回一个包含节点所有祖先节点的迭代器。
p = soup.body.p
for parent in p.parents:
print(parent.name)
搜索文档树是BeautifulSoup的另一个重点。BeautifulSoup提供了几个搜索方法
(1) .find_all():返回一个满足条件的节点列表。
soup.find_all('p', class_='para1')
soup.find_all('p', {'class': 'para1'}, string='第一段落')
(2) .find():返回第一个满足条件的节点。
soup.find('p', class_='para1')
soup.find('p', {'class': 'para1'}, string='第一段落')
(3) .select():使用CSS选择器语法返回满足条件的节点列表。
soup.select('p.para1')
soup.select('p[class="para1"]')
获取节点的属性和文本内容也是常用的操作。BeautifulSoup提供了下面这些方法:
(1) .get():获取节点的指定属性。
p = soup.find('p', class_='para1')
print(p.get('class'))
(2) .text:获取节点的文本内容。
p = soup.find('p', class_='para1')
print(p.text)
(3) .string:获取节点的文本内容(如果节点只有一个子节点且该子节点是字符串类型)。
p = soup.find('p', class_='para1')
print(p.string)
from bs4 import BeautifulSoup
import requests
url = 'https://www.baidu.com'
html = requests.get(url).text
soup = BeautifulSoup(html, 'html.parser')
title = soup.title.string
print(title)
案例中,我们首先发送了一个GET请求获取百度首页的HTML源代码。然后,我们使用BeautifulSoup来创建一个HTML解析器,并将HTML源代码传给它进行解析。接着,我们通过soup.title.string获取HTML文档中title标签的内容,并打印出结果。
from bs4 import BeautifulSoup
html_doc = """
这是标题
第一段落
第二段落
"""
soup = BeautifulSoup(html_doc, 'html.parser')
for p in soup.body.children:
if p.name == 'p':
print(p.string)
案例中,我们创建了一个HTML字符串,并使用BeautifulSoup来创建一个HTML解析器。然后,我们通过soup.body.children遍历整个文档树,查找所有的p标签,并打印出每个标签的文本内容。
from bs4 import BeautifulSoup
html_doc = """
这是标题
第一段落
第二段落
"""
soup = BeautifulSoup(html_doc, 'html.parser')
p_list = soup.select('p.para1')
for p in p_list:
print(p.text)
案例中,我们创建了一个HTML字符串,并使用BeautifulSoup来创建一个HTML解析器。然后,我们使用CSS选择器’p.para1’搜索文档树,并获取所有满足条件的p标签。最后,我们遍历p列表,并打印出每个标签的文本内容。
好的,接下来我再给出三个代码案例。
import re
from bs4 import BeautifulSoup
html_doc = """
这是标题
第一段落
第二段落
"""
soup = BeautifulSoup(html_doc, 'html.parser')
pattern = re.compile('^p.*?1$') # 匹配所有以p开头并且以1结尾的类名
p_list = soup.find_all(class_=pattern)
for p in p_list:
print(p.text)
案例中,我们使用了Python的re模块来创建了一个正则表达式pattern。然后,我们使用soup.find_all(class_=pattern)来搜索文档树,获取所有满足条件的标签,并遍历列表打印出每个标签的文本内容。
from bs4 import BeautifulSoup
xml_doc = """
1
2008
141100
4
2011
59900
"""
soup = BeautifulSoup(xml_doc, 'xml')
for country in soup.find_all('country'):
print(country['name'])
print(country.rank.text)
print(country.year.text)
print(country.gdppc.text)
for neighbor in country.find_all('neighbor'):
print(neighbor['name'], neighbor['direction'])
案例中,我们创建了一个XML字符串,并使用BeautifulSoup来创建一个XML解析器。然后,我们使用soup.find_all()方法搜索文档树,获取所有满足条件的标签,并遍历它们打印出相关信息。
from bs4 import BeautifulSoup
html_doc = """
这是标题
第一段落
第二段落
"""
soup = BeautifulSoup(html_doc, 'html.parser')
p = soup.find('p', class_='para1')
p['class'] = 'new_class'
print(p)
案例中,我们创建了一个HTML字符串,并使用BeautifulSoup来创建一个HTML解析器。然后,我们使用soup.find()方法搜索文档树,获取第一个满足条件的p标签。接着,我们通过p[‘class’]操作修改了标签的class属性,并打印出修改后的标签。
正则表达式是一种用于匹配字符串的模式。它通过字符组成规则定义了搜索文本中特定模式的方法。Python中的re模块提供了使用正则表达式的功能。
常用的正则表达式元字符:
re模块中常用的函数:
(1) 匹配手机号码
import re
phone_nums = ['13912345678', '13812345678', '13512345678', '13612345678', '13712345678']
pattern = r'^1[3-9]\d{9}$'
for phone_num in phone_nums:
if re.match(pattern, phone_num):
print(f'{phone_num}是一个合法的手机号码')
else:
print(f'{phone_num}不是一个合法的手机号码')
代码演示了如何使用正则表达式匹配手机号码。
首先,我们定义了一个包含多个手机号码的列表,并创建了一个正则表达式对象pattern。该正则表达式匹配以1开头的11位数字字符串,其中第二位数字介于3和9之间。然后,我们使用re.match()方法对每个手机号码进行匹配,并打印结果。
(2) 替换HTML文档中的标签
import re
html_doc = """
这是标题
第一段落
第二段落
"""
pattern = r'<.*?>'
new_doc = re.sub(pattern, '', html_doc)
print(new_doc)
代码演示了如何使用正则表达式替换HTML文档中的标签。
首先,我们定义了一个包含HTML标签的字符串,并创建了一个正则表达式对象pattern。该正则表达式匹配任意HTML标签,并将其替换为空字符串。然后,我们使用re.sub()方法对HTML文档进行替换,并打印结果。
(3) 提取金融数据
import re
text = '2019年GDP增速为7.5%,同比增长0.3个百分点;CPI同比上涨2.5%,环比上涨0.3%。'
pattern1 = r'\d+.\d+%'
pattern2 = r'[A-Z]+'
num_list = re.findall(pattern1, text)
unit_list = re.findall(pattern2, text)
for i in range(len(num_list)):
print(f'{num_list[i]} {unit_list[i]}')
代码演示了如何使用正则表达式提取金融数据。
首先,我们定义了一个包含金融数据的字符串,并创建了两个正则表达式对象pattern1和pattern2。其中,pattern1匹配百分数,pattern2匹配单位符号。然后,我们使用re.findall()方法分别提取百分数和单位符号,并以列表的形式返回。最后,我们使用for循环遍历两个列表,并将相同位置上的元素打印在一起。
代码是一个简单的Python脚本,可以用于统计某个文件夹下所有文本文件中各个单词的出现频率,并输出前十个出现频率最高的单词及其出现次数。在代码中,我们将使用正则表达式来去除标点符号、换行符等非单词字符,以便于单词的准确统计。
import os
import re
from collections import Counter
def get_word_counts(folder_path):
"""
统计指定文件夹中所有文本文件中各个单词的出现频率,并返回一个Counter对象。
"""
word_counter = Counter()
for root, dirs, files in os.walk(folder_path):
for file in files:
if file.endswith('.txt'):
file_path = os.path.join(root, file)
# 读取文本文件内容
with open(file_path, 'r', encoding='utf-8') as f:
text = f.read()
# 使用正则表达式去除标点符号、换行符等非单词字符
pattern = r'\b\w+\b'
words = re.findall(pattern, text)
# 对单词列表进行计数,并将结果更新到Counter对象中
word_counter.update(words)
return word_counter
if __name__ == '__main__':
folder_path = 'test'
word_counter = get_word_counts(folder_path)
# 输出前十个出现频率最高的单词及其出现次数
top_n = 10
print(f'Top {top_n} words:')
for word, count in word_counter.most_common(top_n):
print(f'{word:<10} {count}')
代码中的
get_word_counts()
函数用于统计指定文件夹中所有文本文件中各个单词的出现频率,并返回一个Counter对象。在函数中,我们使用了Python内置的os
和collections
模块,以便于对文件和单词计数进行操作。
os.walk()
方法可以遍历指定文件夹下所有子文件夹中的文件,比如我们指定的folder_path
文件夹。然后,我们对每个文本文件进行读取,并使用正则表达式去除标点符号、换行符等非单词字符,以便于单词的准确统计。最后,我们使用Counter
对象来对单词列表进行计数,并将结果更新到该对象中。
在主程序中,我们调用
get_word_counts()
函数来获取单词计数结果,并输出前十个出现频率最高的单词及其出现次数。在这里,我们使用了most_common()
方法来获取前N个出现频率最高的单词及其出现次数,并使用字符串格式化输出结果。
字体反爬是一种常见的网站反爬手段,即将大部分文本内容通过特定的字体进行加密混淆,以防止爬虫直接抓取数据。通常情况下,爬虫需要先解密字体,然后才能正常获取到文本内容。
常用的字体反爬解密方法有以下几种:
很多网站会使用woff格式的字体文件来渲染文本内容,爬虫需要先下载这些字体文件,并解析出字符与字形之间的对应关系,然后才能正常解密文本内容。
Python中有一个非常优秀的字体解析库叫做fontTools,可以帮助我们轻松地解析字体文件,并生成字形对应表。使用该库可以避免自行解析字体文件所遇到的各种问题。
有些网站提供了在线字体解密工具,如FontSpider、字体反爬插件等,可以帮助我们快速地解密字体。不过,使用这种方法需要注意隐私安全问题。
(1) 解析woff文件
import base64
from fontTools.ttLib import TTFont
# 下载字体文件并保存为base64编码字符串
font_url = 'http://example.com/font.woff'
font_base64 = '...'
# 将base64编码字符串解码并保存到本地
with open('font.woff', 'wb') as f:
font_data = base64.b64decode(font_base64)
f.write(font_data)
# 解析字体文件并获取字形对应表
font = TTFont('font.woff')
cmap = font.getBestCmap()
# 定义替换规则
replace_dict = {
'连': '0',
'': '1',
'': '2',
'': '3',
'': '4',
'': '5',
'': '6',
'': '7',
'倫': '8',
'': '9',
}
# 替换文本内容
text = ''
for key, value in replace_dict.items():
text = text.replace(key, value)
# 输出结果
print(text)
代码演示了如何解析woff文件,并使用字形对应表来解密文本内容。首先,我们将从网站上下载字体文件,并保存为base64编码字符串。然后,我们将该编码字符串解码并保存到本地。接下来,我们使用fontTools库读取字体文件,并获取其中的字形对应表。需要注意的是,不同字体文件对应的字形对应表可能不同,因此需要根据具体情况来确定使用哪个表。
我们定义了一个替换规则字典replace_dict,其中包含了从未解密的字符到明文字符的映射关系。最后,我们使用字符串的replace()方法将未解密的文本内容替换为明文,从而得到结果。
(2) 使用fontTools库
import requests
from io import BytesIO
from fontTools.ttLib import TTFont
# 下载字体文件并用fontTools库读取
font_url = 'http://example.com/font.woff'
font_data = requests.get(font_url).content
font = TTFont(BytesIO(font_data))
# 获取字形对应表
cmap = font.getBestCmap()
# 定义替换规则
replace_dict = {
'连': '0',
'': '1',
'': '2',
'': '3',
'': '4',
'': '5',
'': '6',
'': '7',
'倫': '8',
'': '9',
}
# 解密文本内容
text = ''
for key, value in replace_dict.items():
glyph_id = cmap[int(key[3:-1], 16)]
text = text.replace(key, value)
# 输出结果
print(text)
代码演示了如何使用fontTools库解析字体文件,并生成字形对应表。首先,我们使用requests库从网站上下载字体文件,并使用BytesIO将字节流转换为文件。然后,我们使用fontTools库读取该文件,并获取其中的字形对应表。需要注意的是,通过这种方式获取到的字形对应表可能与其他方式获取到的表略有不同,因此需要进行实验来确定使用哪个表。
我们定义了一个替换规则字典replace_dict,并使用字符串的replace()方法将未解密的文本内容替换为明文,从而得到结果。
(3) 使用在线字体解密工具
import requests
from fontSpider import FontSpider
# 下载字体文件并保存为base64编码字符串
font_url = 'http://example.com/font.woff'
r = requests.get(font_url)
font_base64 = FontSpider(r.content).to_base64()
# 使用在线字体解密工具解密文本内容
text = ''
response = requests.post('http://font-spider.com/api/v1/decrypt', data={'font': font_base64, 'text': text})
result = response.json()
# 输出结果
print(result['data'])
代码演示了如何使用在线字体解密工具来解密文本内容。首先,我们从网站上下载字体文件,并使用FontSpider库将其转换为base64编码字符串。然后,我们使用requests库向在线字体解密工具发送POST请求,并将字体文件和未解密的文本内容作为参数传递。该工具会自动解密文本内容,并返回解密后的结果。最后,我们从响应结果中提取出解密后的文本内容,并输出结果。
需要注意的是,使用在线字体解密工具可能存在隐私安全问题,因此尽量避免在生产环境中使用。
Scrapy是一个基于Python的快速、高效的Web爬虫框架,可用于数据抓取、信息处理以及存储的开发。它是一个专业的爬虫框架,提供了许多必要的功能,如请求调度、数据解析,以及数据存储等。Scrapy可以自动下载网页,并提供了XPath以及CSS选择器等多种方法,支持多线程和分布式爬取,并可以通过插件扩展其功能。
Scrapy的工程具有标准的项目结构,通常包含以下几个文件:
Scrapy的爬虫流程如下:
Scrapy具有以下几个重要组件:
import scrapy
class DoubanMovieSpider(scrapy.Spider):
name = 'douban_movie'
allowed_domains = ['movie.douban.com']
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
for info in response.xpath('//div[@class="info"]'):
yield {
'title': info.xpath('div[@class="hd"]/a/span/text()').extract_first(),
'score': info.xpath('div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()').extract_first(),
'director': info.xpath('div[@class="bd"]/p/text()')[0].strip() if len(info.xpath('div[@class="bd"]/p')) == 2 else '',
'year': info.xpath('div[@class="bd"]/p/text()')[-1].strip().replace('(', '').replace(')', '') if len(info.xpath('div[@class="bd"]/p')) == 2 else info.xpath('div[@class="bd"]/p/text()')[-1].strip()
}
next_page = response.xpath('//span[@class="next"]/a/@href')
if next_page:
yield scrapy.Request(url=response.urljoin(next_page.extract_first()), callback=self.parse)
代码演示了如何使用Scrapy爬取豆瓣电影TOP250的数据。
首先,我们定义了一个名为DoubanMovieSpider的爬虫类,并设置了访问URL。在parse()函数中,我们首先使用XPath选择器来解析电影数据,然后通过yield关键字返回一个Python字典,字典的键是电影标题、评分、导演和年份。接着,我们使用XPath选择器获取下一页的链接,并使用yield关键字发送一个HTTP请求,进入下一页继续执行parse()函数。
import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
class RandomUserAgentMiddleware(UserAgentMiddleware):
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; AS; rv:11.0) like Gecko',
'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
]
def process_request(self, request, spider):
user_agent = random.choice(self.user_agents)
request.headers.setdefault('User-Agent', user_agent)
代码演示了如何使用middlewares设置User-Agent。我们首先创建RandomUserAgentMiddleware类,并继承自Scrapy提供的UserAgentMiddleware类,然后定义user_agents列表,保存多种User-Agent。接着,我们重载process_request()函数,并随机选择一个User-Agent,将其添加到HTTP请求头中。
import pymysql
from scrapy.exceptions import DropItem
class MysqlPipeline(object):
def __init__(self, mysql_host, mysql_db, mysql_user, mysql_pwd, mysql_table):
self.mysql_host = mysql_host
self.mysql_db = mysql_db
self.mysql_user = mysql_user
self.mysql_pwd = mysql_pwd
self.mysql_table = mysql_table
def process_item(self, item, spider):
if item['title'] and item['score']:
db = pymysql.connect(host=self.mysql_host, user=self.mysql_user, password=self.mysql_pwd, db=self.mysql_db)
cursor = db.cursor()
sql = "INSERT INTO %s (title, score, director, year) VALUES ('%s', '%s', '%s', '%s')" % (self.mysql_table, item['title'], item['score'], item['director'], item['year'])
try:
cursor.execute(sql)
db.commit()
except Exception as e:
db.rollback()
raise DropItem("Failed to insert item: {}".format(e))
finally:
db.close()
return item
代码演示了如何将数据写入MySQL数据库。我们首先定义了一个名为MysqlPipeline的类,并继承自一个Scrapy提供的基本管道类。在__init__()函数中,我们从配置文件或命令行参数中获取MySQL的连接参数,包括主机、数据库名、用户名、密码以及数据表名。在process_item()函数中,我们判断需要保存的数据是否为空,并使用pymysql库连接数据库。然后,我们执行SQL插入语句,并在发生错误时进行回滚操作。最后,在finally中关闭数据库连接。