杭州购物网站建设,公司名称变更网站要重新备案吗,企业备案网站内容,北京网站建设在线文章目录 认识网络爬虫HTML页面组成Requests模块get请求与实战效果图代码解析 Post请求与实战代码解析 发送JSON格式的POST请求使用代理服务器发送POST请求发送带文件的POST请求 Xpath解析XPath语法的规则集#xff1a;XPath解析的代码案例及其详细讲解#xff1a;使用XPath解… 文章目录 认识网络爬虫HTML页面组成Requests模块get请求与实战效果图代码解析 Post请求与实战代码解析 发送JSON格式的POST请求使用代理服务器发送POST请求发送带文件的POST请求 Xpath解析XPath语法的规则集XPath解析的代码案例及其详细讲解使用XPath解析HTML文档使用XPath解析XML文档处理命名空间的XPath解析 BeautifulSoup详讲与实战创建BeautifulSoup对象遍历文档树搜索文档树获取节点属性和文本内容 BeautifulSoup 代码案例讲解。解析HTML文档并获取标题 遍历文档树并获取所有段落内容使用CSS选择器搜索文档树使用正则表达式搜索文档树解析XML文档并获取节点信息修改节点属性 正则表达式正则表达式知识点正则表达式案例 正则表达式实战字体反爬Scrapy入门Scrapy实例 完结 认识网络爬虫 网络爬虫是指一种程序自动获取网页信息的方式它能够自动化地获取互联网上的数据。通过使用网络爬虫我们可以方便地获取到网络上的各种数据例如网页链接、文本、图片、音频、视频等等。
HTML页面组成 网页是由HTML标签和内容组成HTML标签通过标签属性可以定位到需要的内容。网页中的样式由CSS控制JavaScript可以实现网页动态效果。
HTML标签是一种用于构建Web页面的标记语言它描述了页面的结构和元素。HTML标签通常包含一个起始标签和一个结束标签例如div和/div。HTML标签也可以包含属性属性用于提供有关元素的额外信息。例如a元素的href属性指定了链接目标的URL地址而img元素的src属性指定了要显示的图像文件的URL地址。
CSS是一种用于控制Web页面样式的样式表语言它可以为HTML元素提供样式和布局。通过CSS我们可以控制文本的字体、颜色、大小和样式以及元素的大小、位置、边框和背景等。
JavaScript是用于实现Web页面动态效果的一种编程语言它可以实现网页上的各种交互效果例如弹出窗口、表单验证、动画效果等。
Requests模块get请求与实战 Requests是Python中的HTTP库提供了简洁易用的接口进行HTTP请求。其中GET请求常用于获取静态网页信息。
我们通过requests.get()方法来发送一个GET请求.
import requestsurl 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请求与实战
POST请求与GET请求的区别在于POST请求会将请求参数放在请求体中而GET请求则将请求参数放在URL中。通常情况下POST请求比GET请求更安全因为它可以隐藏请求参数。
我们通过requests.post()方法来发送一个POST请求下面是详细的代码分析
import requestsurl http://xxxx.org/post # 这里使用xxxx.org来演示POST请求
data {key1: value1, key2: value2}
response requests.post(url, datadata)
print(response.text)代码解析
第一行导入了requests模块 第二行指定了要请求的URL地址 。 第三行定义了请求参数data这个字典中包含了两个键值对分别表示key1和key2这两个参数的值。第四行使用requests库的post()方法来发送POST请求并获取响应对象。 我们通过data参数将请求参数放在请求体中这里使用了字典类型作为请求参数。第五行使用print()函数打印出响应内容的文本形式。运行这段代码我们就可以在终端中看到xxxx.org返回的响应内容其中包括了我们发送的请求参数。 在实际爬虫中我们可以利用requests模块的一些属性或者方法来解析响应内容提取需要的数据。
发送JSON格式的POST请求
import requests
import jsonurl http://xxxx.org/post # 这里使用xxxx.org来演示POST请求
data {key1: value1, key2: value2}
headers {Content-Type: application/json}
response requests.post(url, datajson.dumps(data), headersheaders)
print(response.text)在这个案例中我们将请求参数data转换成JSON格式并使用headers来指定Content-Type为application/json。然后我们通过requests库的post()方法来发送POST请求。
使用代理服务器发送POST请求
import requestsproxy {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, datadata, proxiesproxy)
print(response.text)在这个案例中我们通过proxy字典来指定代理服务器的地址和端口号。然后我们通过requests库的post()方法来发送POST请求。 发送带文件的POST请求
import requestsurl http://xxxx.org/post # 这里使用xxxx.org来演示POST请求
files {file: open(myfile.txt, rb)}
response requests.post(url, filesfiles)
print(response.text)在这个案例中我们通过files参数来指定要上传的文件。 open()函数打开文件第一个参数是文件名第二个参数是打开方式rb表示二进制只读模式。然后我们通过requests库的post()方法来发送POST请求。 Xpath解析 XPath是一种用于选择XML文档中某些部分的语言。在Python中我们可以使用lxml库来解析XML文档并使用XPath进行选择。 XPath语法主要由路径表达式和基本表达式构成。其中路径表达式用于选择节点或者节点集合而基本表达式用于指定某个元素、属性或者其他内容。 XPath语法的规则集
表达式描述nodename选择所有名为nodename的元素/从当前节点选取根节点//从当前节点选取任意节点.选择当前节点…选择当前节点的父节点选择属性*匹配任何元素节点[attrib]选择具有给定属性的所有元素[attrib‘value’]选择具有给定属性值的所有元素tagname[text() ‘text’]选择具有给定文本的所有tagname元素
XPath解析的代码案例及其详细讲解
使用XPath解析HTML文档
from lxml import etree
import requestsurl 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语句返回的结果。 使用XPath解析XML文档
from lxml import etreexml
bookstorebook categorycookingtitle langenEveryday Italian/titleauthorGiammarco Tomaselli/authoryear2010/yearprice30.00/price/bookbook categorychildrentitle langenHarry Potter/titleauthorJ.K. Rowling/authoryear2005/yearprice29.99/price/book
/bookstore
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语句返回的结果。 处理命名空间的XPath解析
from lxml import etreexml
ns:bookstore xmlns:nshttp://www.example.comns:book categorycookingns:title langenEveryday Italian/ns:titlens:authorGiammarco Tomaselli/ns:authorns:year2010/ns:yearns:price30.00/ns:price/ns:bookns:book categorychildrenns:title langenHarry Potter/ns:titlens:authorJ.K. Rowling/ns:authorns:year2005/ns:yearns:price29.99/ns:price/ns:book
/ns:bookstore
selector etree.XML(xml)
ns {ns: http://www.example.com}
result selector.xpath(//ns:book[1]/ns:title/text(), namespacesns)
print(result[0])案例中我们定义了一个带有命名空间的XML字符串并使用etree.XML()方法来创建一个XPath解析器。然后我们通过传递一个namespaces参数来指定命名空间的前缀和URI。最后我们使用XPath表达式’//ns:book[1]/ns:title/text()来选择第一个book元素的title元素的内容。最后我们打印出XPath语句返回的结果。 BeautifulSoup详讲与实战 BeautifulSoup是常用的Python第三方库它提供了解析HTML和XML文档的函数和工具。使用BeautifulSoup可以方便地遍历和搜索文档树中的节点获取节点属性和文本内容等信息 创建BeautifulSoup对象
首先我们需要导入BeautifulSoup模块
from bs4 import BeautifulSoup使用BeautifulSoup对HTML文档进行解析可以通过以下两种方式
(1) 传递一个HTML字符串作为参数
html_doc
html
headtitle这是标题/title/head
body
p classpara1第一段落/p
p classpara2第二段落/p
/body
/html
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[classpara1])获取节点属性和文本内容 获取节点的属性和文本内容也是常用的操作。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)BeautifulSoup 代码案例讲解。
解析HTML文档并获取标题
from bs4 import BeautifulSoup
import requestsurl 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 BeautifulSouphtml_doc
html
headtitle这是标题/title/head
body
p classpara1第一段落/p
p classpara2第二段落/p
/body
/html
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标签并打印出每个标签的文本内容。 使用CSS选择器搜索文档树
from bs4 import BeautifulSouphtml_doc
html
headtitle这是标题/title/head
body
p classpara1第一段落/p
p classpara2第二段落/p
/body
/html
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 BeautifulSouphtml_doc
html
headtitle这是标题/title/head
body
p classpara1第一段落/p
p classpara2第二段落/p
/body
/html
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)来搜索文档树获取所有满足条件的标签并遍历列表打印出每个标签的文本内容。 解析XML文档并获取节点信息
from bs4 import BeautifulSoupxml_doc
?xml version1.0 encodingUTF-8?
datacountry nameLiechtensteinrank1/rankyear2008/yeargdppc141100/gdppcneighbor nameAustria directionE/neighbor nameSwitzerland directionW//countrycountry nameSingaporerank4/rankyear2011/yeargdppc59900/gdppcneighbor nameMalaysia directionN//country
/data
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 BeautifulSouphtml_doc
html
headtitle这是标题/title/head
body
p classpara1第一段落/p
p classpara2第二段落/p
/body
/html
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模块提供了使用正则表达式的功能。 常用的正则表达式元字符 . 表示任意字符。\d表示数字\D表示非数字。\w表示单词字符即az、AZ、0~9和下划线。\W表示非单词字符。\s表示空白符包括空格、制表符、换行符等。\S表示非空白符。^表示匹配行首。$表示匹配行尾。*表示匹配前面的字符零次或多次。表示匹配前面的字符一次或多次。?表示匹配前面的字符零次或一次。{m}表示匹配前面的字符m次。{m,n}表示匹配前面的字符m到n次。[…]表示匹配方括号中任意一个字符。[^…]表示匹配除了方括号中给出的字符以外的任意一个字符。(…)表示匹配括号中的表达式。 re模块中常用的函数 re.match()从字符串的开头开始匹配只匹配一次。re.search()在字符串中匹配第一个符合条件的内容。re.findall()在字符串中匹配所有符合条件的内容并以列表的形式返回。re.sub()用一个新的字符串替换掉匹配到的所有内容。re.compile()将正则表达式转化为一个正则表达式对象以便于复用。
正则表达式案例
(1) 匹配手机号码
import rephone_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 rehtml_doc
html
headtitle这是标题/title/head
body
p classpara1第一段落/p
p classpara2第二段落/p
/body
/html
pattern r.*?
new_doc re.sub(pattern, , html_doc)
print(new_doc)代码演示了如何使用正则表达式替换HTML文档中的标签。 首先我们定义了一个包含HTML标签的字符串并创建了一个正则表达式对象pattern。该正则表达式匹配任意HTML标签并将其替换为空字符串。然后我们使用re.sub()方法对HTML文档进行替换并打印结果。 (3) 提取金融数据
import retext 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 Counterdef 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, encodingutf-8) as f:text f.read()# 使用正则表达式去除标点符号、换行符等非单词字符pattern r\b\w\bwords re.findall(pattern, text)# 对单词列表进行计数并将结果更新到Counter对象中word_counter.update(words)return word_counterif __name__ __main__:folder_path testword_counter get_word_counts(folder_path)# 输出前十个出现频率最高的单词及其出现次数top_n 10print(fTop {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文件
很多网站会使用woff格式的字体文件来渲染文本内容爬虫需要先下载这些字体文件并解析出字符与字形之间的对应关系然后才能正常解密文本内容。
使用fontTools库
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 {#x8FDE;: 0,#xE4CD;: 1,#xFAF5;: 2,#xEA72;: 3,#xEDB4;: 4,#xF640;: 5,#xF62F;: 6,#xEB10;: 7,#xF9D4;: 8,#xF15C;: 9,
}# 替换文本内容
text #xE4CD;#xF15C;#xF15C;
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 {#x8FDE;: 0,#xE4CD;: 1,#xFAF5;: 2,#xEA72;: 3,#xEDB4;: 4,#xF640;: 5,#xF62F;: 6,#xEB10;: 7,#xF9D4;: 8,#xF15C;: 9,
}# 解密文本内容
text #xE4CD;#xF15C;#xF15C;
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 #xE4CD;#xF15C;#xF15C;
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入门 Scrapy是一个基于Python的快速、高效的Web爬虫框架可用于数据抓取、信息处理以及存储的开发。它是一个专业的爬虫框架提供了许多必要的功能如请求调度、数据解析以及数据存储等。Scrapy可以自动下载网页并提供了XPath以及CSS选择器等多种方法支持多线程和分布式爬取并可以通过插件扩展其功能。 工程结构
Scrapy的工程具有标准的项目结构通常包含以下几个文件
scrapy.cfgScrapy项目配置文件。items.py定义爬取的数据结构。middlewares.py管理请求和响应例如User-Agent、代理等。pipelines.py配置到底怎么样后续处理item。settings.py保存爬虫的参数设置。spiders/保存爬虫代码的目录。
爬虫流程
Scrapy的爬虫流程如下
发起请求通过定义好的URL地址来发送HTTP请求。下载页面Scrapy会自动下载对应的页面或使用第三方库如requests、Selenium等。解析页面使用XPath或CSS选择器解析网页内容。保存数据将解析得到的数据保存到本地或数据库中。
Scrapy组件
Scrapy具有以下几个重要组件
Spider定义如何抓取某个站点包括如何跟进链接、如何分析页面内容等。Item定义爬取的数据结构。Pipeline负责处理Item如清理、过滤、存储到数据库等。Downloader负责下载网页并将结果传递给Spider。Scheduler负责调度Spider发起请求并将结果传递给Downloader。
Scrapy实例
爬取豆瓣电影TOP250的数据
import scrapyclass DoubanMovieSpider(scrapy.Spider):name douban_movieallowed_domains [movie.douban.com]start_urls [https://movie.douban.com/top250]def parse(self, response):for info in response.xpath(//div[classinfo]):yield {title: info.xpath(div[classhd]/a/span/text()).extract_first(),score: info.xpath(div[classbd]/div[classstar]/span[classrating_num]/text()).extract_first(),director: info.xpath(div[classbd]/p/text())[0].strip() if len(info.xpath(div[classbd]/p)) 2 else ,year: info.xpath(div[classbd]/p/text())[-1].strip().replace((, ).replace(), ) if len(info.xpath(div[classbd]/p)) 2 else info.xpath(div[classbd]/p/text())[-1].strip()}next_page response.xpath(//span[classnext]/a/href)if next_page:yield scrapy.Request(urlresponse.urljoin(next_page.extract_first()), callbackself.parse)代码演示了如何使用Scrapy爬取豆瓣电影TOP250的数据。 首先我们定义了一个名为DoubanMovieSpider的爬虫类并设置了访问URL。在parse()函数中我们首先使用XPath选择器来解析电影数据然后通过yield关键字返回一个Python字典字典的键是电影标题、评分、导演和年份。接着我们使用XPath选择器获取下一页的链接并使用yield关键字发送一个HTTP请求进入下一页继续执行parse()函数。 使用middlewares设置User-Agent
import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddlewareclass 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请求头中。 将数据写入MySQL数据库
import pymysql
from scrapy.exceptions import DropItemclass MysqlPipeline(object):def __init__(self, mysql_host, mysql_db, mysql_user, mysql_pwd, mysql_table):self.mysql_host mysql_hostself.mysql_db mysql_dbself.mysql_user mysql_userself.mysql_pwd mysql_pwdself.mysql_table mysql_tabledef process_item(self, item, spider):if item[title] and item[score]:db pymysql.connect(hostself.mysql_host, userself.mysql_user, passwordself.mysql_pwd, dbself.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中关闭数据库连接。 完结