前言
在学习书本知识时遇到了不会的代码,身为一名热爱学习的有志青年肯定要认真学习搞明白(为了平时分!!!),顺便记录下这次的学习历程。
JS代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var area = page.match(/<tbody>[\s\S]*?<\/tbody>/g)[0]; area = area.replace(/\s/g, '');
var row = []; row = area ? area.match(/<tr[^>]*?>([\s\S]*?)<\/tr>/g) : area;
var colu = [], fundName = [], fundCode = [];
for (var i = 0; i < row.length; i++) { colu[i] = row[i].match(/<td[^>]*?>([\s\S]*?)<\/td>/g); fundName[i] = colu[i] ? colu[i][0] : null; fundCode[i] = colu[i] ? colu[i][1] : null; fundName[i] = fundName[i] ? fundName[i].replace(/<td[^>]*?><a[^>]*?>/g, '').replace(/<\/a><\/td>/g, '') : fundName[i]; fundCode[i] = fundCode[i] ? fundCode[i].replace(/<td[^>]*?>/g, '').replace(/<\/td[^>]*?>/g, '') : fundCode[i]; }
|
正则表达式
语法
/正则表达式主体/修饰符(可选)
修饰符
修饰符 |
描述 |
i |
执行对大小写不敏感的匹配 |
g |
执行全局匹配(查找所有匹配而非在找到第一个匹配后停止) |
m |
执行多行匹配 |
表达式
表达式 |
描述 |
[abc] |
查找方括号之间的任何字符 |
[0-9] |
查找任何从 0 至 9 的数字 |
(x|y) |
查找任何以 | 分隔的选项 |
元字符
元字符 |
描述 |
\d |
查找数字 |
\s |
查找空白字符 |
\b |
匹配单词边界 |
\uxxxx |
查找以十六进制 xxxx 规定的 Unicode 字符 |
量词
量词 |
描述 |
n+ |
匹配任何包含至少一个 n 的字符串 |
n* |
匹配任何包含零个或多个 n 的字符串 |
n? |
匹配任何包含零个或一个 n 的字符串 |
python爬虫爬取数据
爬虫代码
此爬虫爬取的url与实验中的一致,爬取的网页即为JS代码中的page
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import requests import re
class kettle: def __init__(self): self.url = 'http://www.yhfund.com.cn/main/qxjj/index.shtml' self.session = requests.session() self.headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36' } self.session.headers = self.headers
def getHtml(self): self.html = self.session.get(url=self.url, verify=False).text
def save(self): with open(r'./test.html', 'w') as fp : fp.write(self.html)
def start(self): self.getHtml() self.save() def main(): kt = kettle() kt.start()
if __name__ == '__main__': main()
|
爬取的部分数据展示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| <tbody>
<!-- 1.非货币基金产品展示区start -->
<tr>
<th width="20%">基金<br>名称</th>
<th width="7%">基金<br>代码</th>
<th width="7%">更新<br>日期</th>
<th width="7%">单位<br>净值</th>
<th width="8%">累计<br>净值</th>
<th width="7%">最近<br>一周</th>
<th width="7%">最近<br>一月</th>
<th width="7%">最近<br>三月</th>
<th width="7%">最近<br>一年</th>
<th width="7%">今年<br>以来</th>
<th width="9%">成立以来<br>回报率</th>
<th width="8%">交易</th>
</tr>
<tr>
<td><a href="/main/qxjj/180001/fndFacts.shtml">银华优势企业混合</a></td>
<td>180001</td>
<td>11-01</td>
<td>1.7719</td>
<td>3.8327</td>
<!--最近一周-->
<td class="tsred">0.53%</td>
<!--最近一个月-->
<td class="tsred">4.82%</td>
<!--最近三个月-->
<td class="tsred">0.24%</td>
<!--最近一年-->
<td class="tsred">9.99%</td>
<!--今年以来-->
<td class="tsgreen">-0.01%</td>
<!--成立以来-->
<td class="tsred">760.68%</td>
<td><a class="btn_buy2" href="https://trade.yhfund.com.cn/yhxntrade/trade/buyPre.do?fundCode=180001" target="_blank">购买</a></td> </tr>
<tr>
<td> <a href="/main/qxjj/180003/fndFacts.shtml">银华-道琼斯88指数</a> </td> ...... </tr> </tbody>
|
代码解析
Code 1
1 2
| var area = page.match(/<tbody>[\s\S]*?<\/tbody>/g)[0]; area = area.replace(/\s/g, '');
|
此代码作用为定位区域,定位的区域为数据所在的区域,即为爬虫爬取的部分数据展示
page为爬取到的网页,在网页源码中利用正则表达式定位数据所在的代码块
/<tbody><\/tbody>/
:源代码中该区域以<tbody></tbody>
为界限, \/
此处作用是将 /
标记为原意字符,即匹配<tbody></tbody>
[\s\S]*?
:表示匹配任何包含零个或多个空白和非空白字符的字符串,?
表示非贪婪模式。以下举例说明?
作用:
1 2 3 4 5 6 7 8 9 10 11
| // 源代码 <tbody> <tr> <td>数据</td> </tr> </tbody> <tbody> <tr> <td>数据</td> </tr> </tbody>
|
1 2 3 4
| // 加?后匹配代码 <tr> <td>数据</td> </tr>
|
1 2 3 4 5 6 7 8 9
| // 不加?匹配代码 <tr> <td>数据</td> </tr> </tbody> <tbody> <tr> <td>数据</td> </tr>
|
g
:执行全局匹配,查找所有匹配而非在找到第一个匹配后停止
[0]
:match[0]相当于match.group(0),获取匹配的第一组内容,即[\s\S]*?
匹配的内容
/\s/g
:整个area匹配空白字符,替换为’’
Code 2
1 2
| var row = []; row = area ? area.match(/<tr[^>]*?>([\s\S]*?)<\/tr>/g) : area;
|
获取行数,即数据量,一行代表一条完整数据,在源代码中一对<tr></tr>
代表一行完整的数据
[^>]*?
:非贪心的匹配任何包含零个或多个 非>
的字符串,即匹配<tr xxx>
,由于现在该网页的源代码已经看不出该正则的作用,我猜测在几年前该网页的源代码中可能有类似标签<tr bgcolor="#FF0000">
([\s\S]*?)
:非贪心的匹配零个或多个空白和非空白字符,加括号的作用是可以将该括号内匹配的数据单独分配在一组,方便使用
g
:执行全局匹配,查找所有匹配而非在找到第一个匹配后停止
源代码中一对<tr></tr>
代表一行数据,该正则表达式作用就是匹配所有的<tr>
,即得到行数
Code 3
1 2 3 4 5 6 7
| var colu = [], fundName = [], fundCode = [];
for (var i = 0; i < row.length; i++) { colu[i] = row[i].match(/<td[^>]*?>([\s\S]*?)<\/td>/g); fundName[i] = colu[i] ? colu[i][0] : null; fundCode[i] = colu[i] ? colu[i][1] : null;
|
循环遍历行数,取出每一行数据的基金名称和基金代码
以下为一行完整数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <tr>
<td><a href="/main/qxjj/180001/fndFacts.shtml">银华优势企业混合</a></td>
<td>180001</td>
<td>11-01</td>
<td>1.7719</td>
<td>3.8327</td>
<!--最近一周-->
<td class="tsred">0.53%</td>
<!--最近一个月-->
<td class="tsred">4.82%</td>
<!--最近三个月-->
<td class="tsred">0.24%</td>
<!--最近一年-->
<td class="tsred">9.99%</td>
<!--今年以来-->
<td class="tsgreen">-0.01%</td>
<!--成立以来-->
<td class="tsred">760.68%</td>
<td><a class="btn_buy2" href="https://trade.yhfund.com.cn/yhxntrade/trade/buyPre.do?fundCode=180001" target="_blank">购买</a></td> </tr>
|
一行数据包含多个属性,即包含多列数据
一对<tr>
标签,包含多个<td>
标签
所以不难理解以下正则表达式的作用:匹配一行数据的所有属性
1
| colu[i] = row[i].match(/<td[^>]*?>([\s\S]*?)<\/td>/g);
|
[^>]*?
:非贪心的匹配任何包含零个或多个 非>
的字符串,即匹配<td xxx>
,如源代码中的<td class="tsred">
([\s\S]*?)
:非贪心的匹配零个或多个空白和非空白字符,加括号的作用是可以将该括号内匹配的数据单独分配在一组,方便使用
g
:执行全局匹配,查找所有匹配而非在找到第一个匹配后停止
1 2 3 4 5 6 7
| colu[0] = [ <td><a href="/main/qxjj/180001/fndFacts.shtml">银华优势企业混合</a></td>, <td>180001</td>, <td>11-01</td>, ...... ]
|
Code 4
1 2
| fundName[i] = fundName[i] ? fundName[i].replace(/<td[^>]*?><a[^>]*?>/g, '').replace(/<\/a><\/td>/g, '') : fundName[i]; fundCode[i] = fundCode[i] ? fundCode[i].replace(/<td[^>]*?>/g, '').replace(/<\/td[^>]*?>/g, '') : fundCode[i];
|
通过colu[0]所展示的数据就不难得出最后四个正则表达式的作用
- 将
<td xxx><a xxx>
替换成’’
- 将
</a></td>
替换成’’,即可得到正确的名称
- 将
<td xxx>
替换成’’
- 将
</td xxx>
替换成’’,即可得到正确的代码
由于水平有限,以上就是对该JS代码的解析了