整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

网页设计|通过特定搜索引擎搜索来自指定网站的内容

任务描述

1.1 输入关键字,通过百度搜索来自指定网站的网页,如凤凰网。

1.2 下拉菜单和输入框组合到一起,即网站地址既可以手动输入,也可以通过下拉菜单选择后自动输入。

2 HTML+CSS+JS代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />

<script language="javascript" type="text/javascript">

function baiduSearch() {

window.open("http://www.baidu.com/baidu?word=" + formmain.kw.value + "&tn=bds&cl=3&ct=2097152&si=" + formmain.sitesearch.value + "&s=on");

//如果用汉语搜索,关键字最好是重新编码:encodeToGb2312(formmain.kw.value)

}

</script>

<style>

.siteselect{

width:168px;

margin-left:-145px;

margin-left:-146px;

}

.iesitesearch{

font-size:14px;

height:16px;

width:145px;

margin-top:0px;

position:absolute;

left:5px;

}

</style>

<title>站内搜索</title>

</head>

<body>

<form id=formmain onsubmit="return checktype(this); search()" name="f" action=JavaScript:>

关键字:<input type="text" size=35 name="wd" id="kw" maxlength="100" title="输入”搜索词“,在下面区域单击某一按钮,即开始搜索!" />&nbsp;网站地址:

<span style="position:relative;">

<span style="margin-left:150px;overflow:hidden;">

<select class="siteselect" onchange="this.parentNode.nextSibling.value=this.value">

<option value="sohu.com">搜狐</option>

<option value="sina.com.cn">新浪</option>

<option value="163.com">网易</option>

<option value="qq.com">腾讯</option>

<option value="ifeng.com">凤凰网</option>

</select>

</span><input id="sitesearch" name="sitesearch" class="iesitesearch" title="在前面输入框中输入“搜索词“后,在此处可输入网址(如sina.com.cn,前面不需要http://www.)或选择某一特定网站,然后单击“谷歌”或“百度”即可开始定向搜索!";>

<!--</span><input...的内容必须写成一行,否则下拉菜单中选择的内容无法显示到输入框-->

</span>

<input name="btnG3" type="button" value="百度" onclick="baiduSearch();" class="sitebai" />

</form>

</body>

</html>

搜索后的显示页面:

xml是基于 libxml2解析库的Python封装。libxml2是使用C语言编写的,解析速度很好,不过安装起来稍微有点复杂。安装说明可以参考(http: //Lxml.de/installation.html),在CentOS7上中文安装说明(http://www.cjavapy.com/article/64/),使用lxml库来解析网络爬虫抓取到的HTML是一种非常高效的方式。lxml的html模块特别适合处理HTML内容,它可以快速解析大型HTML文件,并提供XPath和CSS选择器来查询和提取数据。

参考文档:https://www.cjavapy.com/article/65/

一、可能不合法的html标签解析

从网络上抓取到的html的内容,有可能都是标准写法,标签什么的都闭合,属性也是标准写法,但是有可能有的网站的程序员不专业,这样抓到的html解析就有可能有问题,因此,解析时先将有可能不合法的html解析为统一的格式。避免为后续的解析造成困扰。

1、lxml.html

lxml.html是专门用于解析和处理HTML文档的模块。它基于lxml.etree,但是为HTML文档的特点做了优化。lxml.html能够处理不良形式的HTML代码,这对于解析和爬取网页尤其有用。

>>> import lxml.html
>>> broken_html = '<ul class="body"><li>header<li>item</ul>'
>>> tree = lxml.html.fromstring(broken_html) #解析html
>>> fixed_html = lxml.html.tostring(tree,pretty_print=True)
>>> print fixed_html
<ul class="body">
<li>header</li>
<li>item</li>
</ul>

2、lxml.etree

lxml.etree是lxml库中用于处理XML文档的模块。它基于非常快的XML解析库libxml2,提供了一个类似于标准库xml.etree.ElementTreeAPI的接口,但是在性能和功能性方面要更加强大。lxml.etree支持XPath、XSLT、和Schema验证等高级XML特性。

>>> import lxml.etree
>>> broken_html = '<ul class="body"><li>header<li>item</ul>'
>>> tree = lxml.etree.fromstring(broken_html) #解析html
>>> fixed_html = lxml.etree.tostring(tree,pretty_print=True)
>>> print fixed_html
<ul class="body">
<li>header</li>
<li>item</li>
</ul>

通过以上可以看出,lxml可以正确解析两侧缺失的括号,并闭合标签,但不会额外增加<html>和<body>标签。

二、处理lxml解析出来的html内容

若在html中找到我们想要的内容,用lxml有几种不同的方法,XPath选择器类似Beautiful Soup的find()方法。CSS选择器用法和jQuery中的选择器类似。两种选择器都可以用来查找文档中的元素,但它们各有特点和适用场景。XPath是一种在XML文档中查找信息的语言。它可以用来遍历XML文档的元素和属性。CSS选择器通常用于选择和操作HTML文档中的元素。

1、XPath选择器(/单斜杠表示绝对查找,//双斜杠表示相对查找)

from lxml import etree
source_html = """
         <div>
            <ul>
                 <li class="item-0"><a href="link1.html">first item</a></li>
                 <li class="item-1"><a href="link2.html">second item</a></li>
                 <li class="item-inactive"><a href="link3.html">third item</a></li>
                 <li class="item-1"><a href="link4.html">fourth item</a></li>
                 <li class="item-0"><a href="link5.html">fifth item</a>
             </ul>
         </div>
        """
html = etree.HTML(source_html)
print(html)
result = etree.tostring(html)#会对的html标签进行补全
print(result.decode("utf-8"))

输出结果:

<Element html at 0x39e58f0>
<html><body><div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</li></ul>
</div>
</body></html>

1)获取某个标签的内容(a标签后不需要加斜杠,否则会报错)

#第一种写法

html = etree.HTML(source_html)
html_data = html.xpath('/html/body/div/ul/li/a')#绝对查找
#html_data = html.xpath('//li/a')#相对查找
print(html)
for i in html_data:
    print(i.text)

输出结果:

<Element html at 0x14fe6b8>
first item
second item
third item
fourth item
fifth item

#第二种写法
#在要找的标签后面加/text(),就是获取标签中的文本内容,结果中直接就是文本内容了,不用在通过text属性获取了。

html = etree.HTML(source_html)
html_data = html.xpath('/html/body/div/ul/li/a/text()')#绝对查找
#html_data = html.xpath('//li/a/text()')#相对查找
print(html)
for i in html_data:
    print(i)

输出结果:

<Element html at 0x128e3b7>
first item
second item
third item
fourth item
fifth item

2)获取a标签下的属性

html = etree.HTML(source_html)
html_data = html.xpath('//li/a/@href') #相对查找
#html_data = html.xpath('/html/body/div/ul/li/a/@href') #绝对查找
for i in html_data:
    print(i)

输出结果:

link1.html
link2.html
link3.html
link4.html
link5.html

3)查找a标签属性等于link2.html的内容

html = etree.HTML(source_html)
html_data = html.xpath('/html/body/div/ul/li/a[@href="link2.html"]/text()')绝对查找
#html_data = html.xpath('//li/a[@href="link2.html"]/text()')#相对查找
print(html_data)
for i in html_data:
    print(i)

输出结果:

['second item']
second item

4)查找最后一个li标签里的a标签的href属性

html = etree.HTML(source_html)
html_data = html.xpath('//li[last()]/a/text()')
print(html_data)
for i in html_data:
    print(i)

输出结果:

['fifth item']
fifth item

5)查找倒数第二个li标签里a标签的href属性

html = etree.HTML(source_html)
html_data = html.xpath('//li[last()-1]/a/text()')
print(html_data)
for i in html_data:
    print(i)

输出结果:

['fourth item']
fourth item

6)查找某个标签id属性值等于value的标签

//*[@id="value"]

7)使用chrome浏览器提取某个标签的XPath

2、CSS选择器(基本上和jQuery选择器用法一样)

选择器

描述

*

选择所有标签

a

选择<a>标签

.link

选择所有class = 'link'的元素

a.link

选择class = 'link'的<a>标签

a#home

选择id = 'home'的<a>标签

a > span

选择父元素为<a>标签的所有<span>子标签

a span

选择<a>标签内部的所有<span>标签

使用示例:

>>> html = """<div>
<tr id="places_area_row" class="body">
<td>header</td>
<td class="w2p_fw">item1</td>
<td class="w2p_fw">item2</td>
<td class="w2p_fw">item3</td>
<td><tr><td class="w2p_fw">header</td>
<td class="w2p_fw">item4</td>
<td class="w2p_fw">item5</td>
<td class="w2p_fw">item6</td></tr></td>
</tr>
</div>"""
>>> tree = lxml.html.fromstring(html)
>>> td = tree.cssselect('tr#places_area_row > td.w2p_fw')[0]
>>> htmlText = td.text_content()
>>> print htmlText
item1

参考文档:https://www.cjavapy.com/article/65/

、开发背景

  2年前做了一个基于element-ui的layout组件发布到npm package上去,广受大家的欢迎,下载量每周颇升。这个组件的好处在于开发者不用写太多html代码和逻辑,只要通过配置json的方式就能马上生成后台,包括菜单栏和导航面包屑等一键生成,还能根据不同的需求做定制化后台界面,就像拼积木一样,让用户只专注于内容核心代码的开发。现在还没做大范围的推广,如果我觉得它做得足够好,我一定会放到npm上推荐给各位读者使用哈。当初为了解答网友的疑问,我还专门建立了该组件的官网。logo看起来有没有很熟悉?哈哈哈,它其实就是element-ui的logo进行改造的,意义就是告诉使用者,这是element-ui功能的组合加强版。

  为了迎合公司的OKR,我提出了对表单表格组件的封装。为什么我会有这个想法?其实这个组件我很早就想做了,只是以前做的是基于UI层面的,近期我接手了公司的一个项目叫童画,每天做的事情感觉就是复制粘贴,修改部分不同的功能和字段名称。组件的意义在于可以在遇到同一类设计场景时,可以复用,从而减少设计的时间和形成产品的统一性。传统的搜索表单不就是这样吗?上面是表单搜索字段,中间是搜索结果的表格,下面是搜索结果的分页。把表单和表格组合起来的好处在于很多搜索字段都是基于表格组件的某些字段,那我根据search字段进行筛选不就可以了么,很久之前,我们总是在吐槽产品经理总是喜欢截图现有功能,然后做字段修改,搞成原型扔给前端。如果前端也能像他这样简单,那该多好呀,类似这样的想法油然而生。刚开始我只是为了方便我的工作,没想到领导却重视了起来,想把这个组件推广给公司其他十几个前端同事使用,于是乎,我便认真开搞了起来。有理论还不行,得有场景实践,刚好公司的项目童画有很多场景,我根据它里面的场景,做了很多功能的封装和兼容。

  写到这里,有人会说了,这不就是CRUD组件吗?有这想法的话,说明你还太年轻和小看这个组件的功能了。传统的CRUD组件灵活性不是很高,这个组件的好处是配置即可用,不用考虑其他搜索,翻页,清空等各种逻辑,让组件达到高度复用,封装了场景的插槽类型,但为了防止翻车,我还是预留了变态需求的插槽。字段的使用更多采用elementUI的命名方式,让使用者减少学习成本。这样做的好处是什么呢?首先,前端再也不用写页面了,其次,对于比较规矩的搜索表单页面,完全可以通过请求接口的形式交给后端来配置呈现页面即可,根本没有前端什么事了,前端的工作可以解放出来做更复杂的功能开发。


二、部分原理

  1. 搜索字段通过search=true进行筛选;
  2. 分页通过请求的total总数进行分页;
  3. Vue.prototype.$query接入请求。

三、组件文档

为了使用方便,我把它做成了组件并放到了公司的私服上,接下来的工作就是写文档啦,以下是部分文档的编写,因为时间问题,没来得及好好检查,各位看官将就看一下就行啦。


export default {
options: {
  request: {
    api: '/student/web/student/enroll/list',
    method: 'GET',
    paramMap: {
      index: 'pageIndex',
      limit: 'pageSize'
    },
    resultMapping: {
      total: 'total',
      data: 'pageData'
    }
  },
  size: '',  // medium/mini/small, 默认medium
  labelWidth: 90,
  submitBtn: true, // 搜索按钮,默认true,非必填
  submitText: '查询', // 搜索按钮的文字,默认查询,非必填
  clearBtn: true, // 清除按钮,默认true
  clearSize: 'mini', // medium/mini/small, 默认medium
  clearText: '清除', // 清除按钮的文字,默认清除,非必填
  column: [
    {
       slotType: 'selection'
    },
    {
      prop: 'keyword',
      label: '学员/家长',
      search: true,
      hide: true
    },
    {
      prop: 'studentName',
      label: '学员'
    },
    {
      prop: 'telephone',
      label: '家长手机号',
      width: 130
    },
    {
      prop: 'type',
      label: '报名类型',
      width: 90,
      search: true,
      type: 'select',
      slot: true,
      slotType: 'text',
      slotArray: [{
        label: '新报',
        value: 1
      },
      {
        label: '续报',
        value: 2
      }],
      dicData: [{
        label: '新报',
        value: 1
      },
      {
        label: '续报',
        value: 2
      }]
    },
    {
      prop: 'courseFee',
      label: '缴费金额',
      slot: true,
      slotType: 'regEx',
      regEx: '¥{{courseFee/100}}'
    },
    {
      prop: 'payType',
      label: '支付方式',
      search: true,
      type: 'select',
      width: 100,
      dicData: [
        {
          label: '微信',
          value: '微信'
        },
        {
          label: '支付宝',
          value: '支付宝'
        },
        {
          label: '银行卡转账',
          value: '银行卡转账'
        },
        {
          label: '其它',
          value: '其它'
        }
      ]
    },
    {
      prop: 'courseCount',
      label: '报名课时'
    },
    {
      prop: 'followTeacher',
      label: '跟进人'
    },
    {
      prop: 'createTime',
      label: '报名时间',
      param: 'beginTime,endTime',
      format: 'yyyy 年 MM 月 dd 日',
      valueFormat: 'timestamp',
      search: true,
      width: 160,
      type: 'daterange'
    },
    {
      prop: 'auditor',
      label: '报名老师',
      search: true,
      param: 'teacherId', // 修正请求参数名
      type: 'select',
      dicData: [],
      dicUrl: '/org/web/org/user/list/teacher',
      dicMap: {
        label: 'userName',
        value: 'id'
      }
    },
    {
      prop: 'status',
      label: '报名状态',
      slot: true,
      slotType: 'tag',
      width: 110,
      slotArray: [{
        type: 'warning',
        label: '待审核',
        value: 1
      },
      {
        type: 'danger',
        label: '审核不通过',
        value: 3
      },
      {
        type: 'success',
        label: '审核通过',
        value: 2
      }],
      dicUrl: ''
    },
    {
      prop: 'operation',
      label: '操作',
      width: 80,
      slot: true,
      slotType: 'operation',
      slotArray: [
        {
          label: '去审核',
          value: 'handle',
          filter: ({status}) => {
            return status === 1
          }
        }
      ]
    }
  ]
}
}


生成的页面

四、最后感谢

最后,感谢同事世丞在字段命名上给了很多建议,同时也感谢领导给了很多刁难的意见,让这个组件的功能越发强大,也让开发者使用更方便,达到配置即可使用的地步。当然了,组件还需要更多场景的训练才能真正实现各种功能的兼容。后期我希望可以尽快放到layout官网上,供大家使用。


五、关于作者

一个会美工与后端PHP/nodejs的全栈工程师

更多学习内容欢迎关注

微信公众号 :程序员周先生