Java教程

WEB编程Javascript实验报告

本文主要是介绍WEB编程Javascript实验报告,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

写给广大网友: 如果不是为了作业我甚至不会写这篇博客, 这篇博客没有任何技术参考价值, 反而可能给各位造成误导, 请各位慎重参考莫要完全置信! 

写给助教: 大多数内容是我学习笔记直接Paste上来的...未必代表我的学习顺序. 

任务: 爬取网页

小任务: 学会使用request

request的第一个参数包括请求头, url, 编码方式等信息, 代表请求; 第二个参数代表回调函数, 其中body部分相当于网页中的document对象

//重新包装后柯里化的request函数
let my_req = (callback) => (url) => {
    let options = {
        url: url,
        encoding: null,
        headers: headers,
        timeout: 1000
    }
    try {
        request(options, (error, response, body) => {//request的第一个参数包括请求头, url, 编码方式等信息, 代表请求; 第二个参数代表回调函数, 其中body部分相当于网页中的document对象
            try {
                if (!error && response.statusCode === 200)
                    callback(body);
                else {
                    console.error("访问 " + url + " 超时, 原因可能是网络不佳或爬虫被屏蔽  " + error + " " + response);
                    fails++, console.log("有" + fails + "次请求中的失败");
                }
            } catch (e) { console.log("回调" + url + " 时出现错误 " + e) }
        })
    } catch (e) { console.error("访问不成功, 可能是非法的url格式, 或回调函数中出现了错误" + e); }
}

小任务: 用正则表达式匹配新闻网页的url

分支小任务: 初步掌握Js类和对象

Js是一种动态语言, 因此它的对象并不是通过声明来固定某种类型, 而是通过调用构造函数生成对象并将它赋值给某个变量. 

对象的成员变量(或叫属性)和函数和

分支小任务: 获得一个正则表达式

只需要调用正则表达式的构造函数或者通过简便写法'/a/'就能

var regexp = new RegExp("a");
var regexp_ = /a/;

正则表达式能够作为字符对象match函数的参数(模式串)使用, match函数的返回值是一个子串, 使得其存在从第0个字符开始的子串能够与模式串匹配. 

分支小任务: 正则表达式的匹配逻辑

由于在Leetcode上写过正则表达式那个题, 所以我对两种特殊的模式串'.'和'*'还是比较熟悉的. 

bool isMatch(string &s, string &p, int _s = 0, int _p = 0) {
    if (_p + 1 < p.size() && p[_p + 1] == '*') {
        if (isMatch(s, p, _s, _p + 2)) return true;
        for (int i = 1; _s + i - 1 < s.size(); i++) {
            if (p[_p] != '.' && p[_p] != s[_s + i - 1]) break;
            if (isMatch(s, p, _s + i, _p + 2)) return true;
        }
        return false;
    }
    if (_s >= s.size() && _p >= p.size())  return true;
    if (_s >= s.size() || _p >= p.size())  return false;
    return (p[_p] == '.' || p[_p] == s[_s]) && (isMatch(s, p, _s + 1, _p + 1));
}

能匹配一类字符的特殊字符: '\d'匹配所有数字和'\w'匹配所有字母; '[abc]'匹配方括号内的任意一个字符. 

决定匹配次数的特殊字符: '*'匹配它前面的字符任意次(包括0次); '?'匹配它前面的字符0次或1次; '+'匹配它前面的字符1次或更多次. 

决定匹配开始和结束的特殊字符: '^'待匹配串的开头是模式串; '$'它之前的待匹配串的结尾是模式串. 

分支小任务: 解析新闻网页的url并写出正确的正则表达式

一个网页是新浪主页上的新闻网页, 当网页的url不是.html就是.shtml或者.phtml结尾, 且用https:/作为开头. 

var news_reg = /https?:\/\/[\S]*.s?p?html$/;

分支小任务: 通过种子页面将url传给负责处理新闻页面的函数

其中check函数是通过上面的正则表达式实现的, 过于trivial于是不贴chek函数的代码. 

const open_all_ahref_with = (open_) => my_req((body) => {
    try {
        var html = myIconv.decode(body, my_encoding);
        var $$ = myCheerio.load(html, { decodeEntities: true });
    } catch (e) { console.log("解析含a链接的网页失败" + e) }

    try { var a_tags, a_tags = $$('a') }
    catch (e) { console.log("解析网页中的a链接失败" + e) }

    if (a_tags == undefined)
        return console.log("没有发现a链接");

    a_tags.each((i, elem) => {
        let href = $$(elem).attr("href");
        href = formated_as_whole_url(href);
        console.log(href);
        if (check(href)) open_(href);
    })
})

小任务: 通过url访问新闻页面

分支小任务: html标签与jQuery选择器

最基础的用法

匹配某个类型的标签

$('a')

分支小任务: jQuery对象方法

遍历使用forEach, 其参数是回调函数, 函数的参数是(i, elem)的二元组, 函数体内放上与正常for循环该有的代码, 当需要循环中断时, 返回false, 需要继续执行, 返回true. 语法与for非常不同, 但功能上提供了break和continue类似的东西. 

获取标签的属性和内容

$('a').text()//第一个a链接中装着的内容
$('a').attr('href')//第一个a链接中的href属性的值

分支小任务: 解析新闻页面并写出处理它的函数

处理新闻的过程非常傻, 基本上完全依赖于网页上实际有啥. 

const store_news_with = (store_) => (url) => my_req((body) => {
    try {
        var html = myIconv.decode(body, my_encoding);
        var $$ = myCheerio.load(html, { decodeEntities: true });
    } catch (e) { console.log("解析新闻页面失败" + e) }
    try {
        var fetch = {
            title: "",
            content: "",
            url: url,
            keywords: "",
            publish_time: "",
            crawl_time: new Date(),
            source_name: seed,
            source_encoding: my_encoding,
        }
    } catch (e) { console.log("定义当前网页的抓取(fetch)对象失败" + e) }
    try {
        var possible_title = $$('title');
        possible_title.each((i, elem) => {
            fetch.title = fetch.title + $$(elem).text() + "\n";
        })
    } catch (e) {
        console.log("没能从title标签中捕获标题" + e);
        try {
            var possible_title = $$('h1');
            possible_title.each((i, elem) => {
                let text = $$(elem).text();
                if (/�/.test(text))
                    return false;
                fetch.title = fetch.title + text + "\n";
            })
        } catch (e) { console.log("没能从h1标签中捕获标题" + e); return; }
    }
    try {
        var possible = $$('p');
        possible.each((i, elem) => {
            let text = $$(elem).text();
            if (/�/.test(text))
                return false;
            if (text.length > 20 && !/\|/.test(text) && !/   /.test(text))
                fetch.content = fetch.content + text + "\n";
        })
    } catch (e) { console.log("没能从p标签中捕获内容" + e); return; }
    try {
        var possible = $$('meta[name="keywords"]').first();
        fetch.keywords = possible.attr("content");
    } catch (e) { console.log("没能捕获关键词" + e); return; }
    try {
        var possible = $$('meta[property*="time"]').first();
        fetch.publish_time = possible.attr("content");
    } catch (e) { console.log("没能从meta标签捕获发行时间" + e) }
    if (fetch.publish_time === "")
        try {
            var possible = $$('span[class*="time"]').first();
            fetch.publish_time = possible.text();
        } catch (e) { console.log("没能从span标签捕获发行时间" + e); return; }
    var no_undefined = true;
    if (fetch == undefined) {
        console.assert("fetch对象为空");
        return;
    }
    try {
        for (let i in fetch)
            if (fetch[i] == "" || fetch[i] == undefined)
                return console.log("fetch对象中的" + i + "为空")
    } catch (e) { console.log("fetch对象不能被迭代" + e), no_undefined = false }
    if (no_undefined)
        store_(fetch);
})(url)

小任务: 将新闻存储到mysql

分支小任务: mysql建表

CREATE TABLE `fetches` (
  `id_fetches` int(11)  NOT NULL AUTO_INCREMENT,
  `url` varchar(200) DEFAULT NULL,
  `source_name` varchar(200) DEFAULT NULL,
  `source_encoding` varchar(45) DEFAULT NULL,
  `title` varchar(200) DEFAULT NULL,
  `keywords` varchar(200) DEFAULT NULL,
  `publish_time` date DEFAULT NULL,
  `crawl_time` datetime DEFAULT NULL,
  `content` longtext,
  `createtime` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id_fetches`),
  UNIQUE KEY `id_fetches_UNIQUE` (`id_fetches`),
  UNIQUE KEY `url_UNIQUE` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

分支小任务: 学习异步函数与Promise对象

异步函数: 在定义函数时使用的function关键字前或者在定义箭头函数的参数列表前加上async关键字就能将这个函数声明为异步函数. 

异步函数中, 各条命令没有执行顺序的限制, 所以它会比非异步的情况更快完成(尤其是在网络请求和渲染异步进行的业务场景中非常必要). 

当某条命令依赖于某个变量的值时, 这个变量很可能没有变成程序员所需要的那样. 此时Promise对象提供了一种解决方案. 

Promise对象的构造函数的参数是一个函数, 这个函数的入参是两个函数resolve和reject, 函数体内包含了一系列的指令, 这些指令最终产生的结果将作为resolve的入参. 

对Promise对象使用await关键字时, 它将返回resolve的入参. 这使得异步编程变得更自然. 

就我的观点而言Promise是某种扭曲的产物, 是Js社区中同时存在函数式编程和对象式编程两种范式的不良后果. 事实上, 纯函数式编程中并不需要Promise, 而纯对象式编程不会这样来定义Promise的构造函数. 

分支小任务: 封装mysql有关的查询方法

首先封装连接池(通过node_module中的mysql组件)

const mysql = require('mysql');

var pool_ = mysql.createPool({
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'crawl'
});

module.exports = { pool_ };

然后封装请求指令: 指令中需要先从池中抽取连接, 再通过连接来进行请求, 故写作异步函数时需要用Promise保证其先执行. (实际上此处完全没有必要, 因为它与非异步的效果几乎相同)

const { pool_ } = require('./pool_');

const findUser = () => {
    return new Promise((resolve, reject) => {
        try {
            pool_.getConnection((err, conn) => {
                if (err) { console.warn("Cannot connect MySql " + err); }
                else { resolve(conn); return; }
            })
        } catch (e) { reject(undefined); }
    })
}

const sqlquery_ = async (sql, param, callback) => {
    let connection = await findUser();
    return new Promise(() => connection.query(sql, param, callback)).then(connection.release());
}

const sqlquery_noparam = async (sql, callback) => {
    let connection = await findUser();
    return new Promise(() => connection.query(sql, callback)).then(connection.release());
}

exports.query_ = sqlquery_;
exports.query_noparam = sqlquery_noparam;

分支小任务: 通过自己写的mysql函数来向mysql写入新闻

const store_to_mysql_if_no_repetition = (fetch) => {
    const fetchAddSql = 'INSERT INTO fetches(url,source_name,source_encoding,title,' +
        'keywords,publish_time,crawl_time,content) VALUES(?,?,?,?,?,?,?,?)';
    const fetchAddSql_Params = [fetch.url, fetch.source_name, fetch.source_encoding,
    fetch.title, fetch.keywords, fetch.publish_time,
    fetch.crawl_time.toFormat("YYYY-MM-DD HH24:MI:SS"), fetch.content
    ];
    sql.query_(fetchAddSql, fetchAddSql_Params, (err, vals, fields) => {
        if (err) {
            console.log("存入MySql时: " + err);
        } else {
            console.log("存储成功:" + fetch.url);
        }
    })
}

任务 实现搜索并用表格展示

小任务: 实现搜索栏

分支小任务: 网页里放点啥标签

首先需要一个表单来向服务器提交数据, 因而有一个<form>标签; 然后搜索数据需要关键字类型(关键词, 标题, 日期, 内容等)就在里面放个<select>标签再在里面套个<option>标签; 

搜索按钮用<input type = 'button'>, 搜索框用<input type = 'text'>. 

需要实现搜索下拉栏, 它是个无序列表, 所以在下面放个<div>再套个<ul>, 拿到数据之后就放到里面去. 

    <div class="wrapper">
        <img src="Images/DASE.png" class="logo">
        <form class="SearchBox">
            <div class="MainBox" style="display: block;">
                <select class="SearchOptions">
                    <option value="title">Title</option>
                    <option value="keywords">KeyWords</option>
                    <option value="publish_time">Publish Time</option>
                    <option value="source_name">Source</option>
                    <option value="crawl_time">Crawl Time</option>
                </select>
                <input type="text" class="SearchTextBox">
                <input type="button" value="Search" class="SearchButton">
            </div>
            <div class="Sublist" style="display: inline-block;">
                <ul class="Suggestion">
                </ul>
            </div>
        </form>
    </div>

分支小任务: css样式设置

令人欣慰的是: css的选择器和jQuery的选择器语法居然大差不差, 省掉了我很多力气. 

伪类选择器的关键字多了个:hover用来判断鼠标悬停事件, 由此就能做出下拉框的动态效果了. 

    <style>
        .wrapper {
            text-align: center;
            margin-top: 150px;
            z-index: 0;
        }

        .logo {
            display: inline-block;
            margin-left: 10px;
            margin-block-end: 30px;
            width: 375px;
            height: auto;
        }

        .MainBox {
            text-align: center;
            display: block;
        }

        .SearchBox {
            display: block;
        }

        .SearchOptions {
            display: inline-flex-box;
            vertical-align: top;
            position: relative;
            left: 6px;
            width: 115px;
            height: 16px;
            z-index: 10;
            padding: 12px 16px;
            font-size: 16px;
            margin: 0 0 0 0;
            outline: 0;
            box-shadow: none;
            border-radius: 10px 0 0 10px;
            border: 2px solid #3e0079;
            background: #fff;
            color: #222;
            box-sizing: content-box;
            text-align: center;
        }

        .SearchTextBox {
            display: inline-flex;
            vertical-align: top;
            width: 512px;
            height: 16px;
            padding: 12px 16px;
            margin: 0, 0, 0, 0;
            font-size: 16px;
            box-shadow: none;
            border-radius: 0 0 0 0;
            border: 2px solid #3e0079;
            background: #fff;
            color: #222;
            box-sizing: content-box;
        }

        .SearchButton {
            display: inline-flexbox;
            z-index: 100;
            vertical-align: top;
            position: relative;
            left: -6px;
            cursor: pointer;
            width: 108px;
            height: 44px;
            line-height: 45px;
            line-height: 44px\9;
            padding: 0;
            margin: 0, 0, 0, 0;
            background: 0 0;
            background-color: #3e0079;
            border-radius: 0 10px 10px 0;
            font-size: 17px;
            color: #fff;
            box-shadow: none;
            font-weight: 400;
            border: none;
            outline: 0;
        }

        .Suggestion {
            display: inline-block;
            position: relative;
            left: -17px;
            top: -17px;
            list-style: none;
            width: 512px;
            height: auto;
            margin: 0, 0, 0, 0;
        }

        .SuggestionWord {
            display: block;
            width: 512px;
            height: auto;
            padding: 12px 16px;
            margin: 0, 0, 0, 0;
            font-size: 16px;
            border: 2px solid #3e0079;
            border-top: none;
            background: rgb(253, 250, 255);
            color: #222;
        }


        div.SuggestionWord:hover {
            background-color: rgb(243, 229, 243);
            color: #3e0079;
        }

        .SuggestionWord:first-child {
            border-top: 2px solid #3e0079;
        }

        .SuggestionWord:last-child {
            border-radius: 0 0 10px 10px;
        }

        a {
            text-decoration: none;
            z-index: 100;
        }
    </style>

由于某种未知的原因

这篇关于WEB编程Javascript实验报告的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!