android中小说信息抓取展示

时间:2024-02-20 17:54:46

1、引言

        这两天打算把以前弄的一个小说阅读软件的爬虫规则更新一下,然后略微修改后准备上线使用,目前只改了一下搜索界面的规则,勉强能用,加载数据方面还需要优化一下,把这一部分发出来给大家看看,感兴趣的可以直接复制去使用。

2、主要代码

2.1、主界面布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ui.SearchActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#f7e7c5"
        android:orientation="horizontal">
        <LinearLayout
            android:id="@+id/ll_search"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center_vertical"
            android:layout_marginBottom="10dp"
            android:orientation="horizontal">
            <LinearLayout
                android:id="@+id/ll_exit"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <ImageView
                    android:id="@+id/iv_exit"
                    android:layout_width="9dp"
                    android:layout_height="16dp"
                    android:layout_marginHorizontal="25dp"
                    android:layout_marginVertical="10dp"
                    android:background="@drawable/back"/>
            </LinearLayout>
            <LinearLayout
                android:id="@+id/ll_search_top"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:gravity="center_vertical"
                android:background="@drawable/shape_search"
                android:layout_weight="1">
                <ImageView
                    android:id="@+id/iv_circle"
                    android:layout_width="19dp"
                    android:layout_height="20dp"
                    android:layout_marginHorizontal="10dp"
                    android:background="@drawable/search_edit"/>
                <EditText
                    android:id="@+id/et_content"
                    android:layout_width="match_parent"
                    android:layout_height="34dp"
                    android:singleLine="true"
                    android:textSize="20sp"
                    android:imeOptions="actionSearch"
                    android:background="#00000000"
                    android:hint="请输入书名或者作者"/>
            </LinearLayout>
            <TextView
                android:id="@+id/tv_search"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20sp"
                android:textColor="#303030"
                android:layout_marginHorizontal="10dp"
                android:text="搜索"/>
        </LinearLayout>
    </LinearLayout>
    <ListView
        android:id="@+id/lv_search"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="#00000000"/>
    <WebView
        android:id="@+id/wv_search"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"/>
</LinearLayout>

2.2、ListView布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv_search_name"
        android:layout_width="95dp"
        android:layout_height="136dp"
        android:layout_marginTop="20dp"
        android:visibility="invisible"
        android:layout_marginHorizontal="20dp"
        android:background="#a88d8a"/>
    <LinearLayout
        android:id="@+id/ll_item_search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_search_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:textSize="20sp"
            android:layout_marginTop="5dp"
            android:textColor="#303030"
            android:textStyle="bold"
            android:text="书名"/>
        <TextView
            android:id="@+id/tv_search_author"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="5dp"
            android:textSize="15sp"
            android:textColor="#a5a5a5"
            android:text="作者:"/>
        <TextView
            android:id="@+id/tv_search_introduction"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:textSize="15sp"
            android:layout_marginTop="10dp"
            android:maxLines="3"
            android:visibility="invisible"
            android:ellipsize="end"
            android:text="简介:"/>
    </LinearLayout>
</LinearLayout>

2.3、activity界面代码

package xyz.dritrtj.read.ui;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import xyz.dritrtj.read.R;
import xyz.dritrtj.read.data.Title;
import xyz.dritrtj.read.interfaces.Init;
import xyz.dritrtj.read.utils.SetUiSize;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * java中重复使用的变量使用后一定要赋值为空,否则数据可能会出错
 */
public class SearchActivity extends AppCompatActivity implements View.OnClickListener, Init {
    private LinearLayout ll_search;
    private LinearLayout ll_exit;
    private ImageView iv_exit;
    private ImageView iv_circle;
    private EditText et_content;
    private TextView tv_search;
    private ListView lv_search;
    private LinearLayout ll_search_top;
    private WebView wv_search;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);
        initView();
        setViewSize();
        setData();
    }
    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.tv_search:
                handler.sendEmptyMessage(3);//通知搜索
                break;
            case R.id.ll_exit:
                handler.removeMessages(0);
                finish();
                break;
        }
    }
    @SuppressLint("HandlerLeak")
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 0:
                    if (list.size()>0){
                        lv_search.setAdapter(adapter);
                    }
                    break;
                case 1://通知更新数据,不要用线程更新
                    adapter.notifyDataSetChanged();
                    break;
                case 2://页面加载完成通知可以开始搜索
                {
                    String name=et_content.getText().toString();
                    et_content.setText("");
                    if (!name.equals("")){
                        jsFunction="javascript:function search(data) {\n" +
                                "                var search=document.getElementById('bdcsMain');\n" +
                                "                search.value=''+data;\n" +
                                "                var cls=document.getElementsByClassName('searchBtn');\n" +
                                "                if (cls.length>0){\n" +
                                "                    var now_search=cls[0];\n" +
                                "                    now_search.click();\n" +
                                "                }\n" +
                                "            }";
                        wv_search.loadUrl(jsFunction);
                        //调用 js函数
                        wv_search.loadUrl("javascript:search("+"'"+name+"'"+");");
                    }
                }
                    break;
                case 3://重新刷新页面,准备搜索
                {
                    flag=0;
                    isStart=false;
                    isSearch=true;
                    wv_search.clearCache(true);//清除缓存
                    wv_search.loadUrl("http://www.ibiquzw.info/");//重新加载页面,因为后面会替换掉url
                }
                    break;
                case 4://返回搜索数据
                {
                    jsFunction="javascript:function getTitle() {\n" +
                            "                var uls=document.getElementsByTagName('ul');\n" +
                            "                if(uls.length>0){\n" +
                            "                    ul=uls[1];\n" +
                            "                    var lis=ul.getElementsByTagName('li');\n" +
                            "                    if(lis.length>0){\n" +
                            "                        var now_li;\n" +
                            "                        var as;\n" +
                            "                        var a;\n" +
                            "                        var url;\n" +
                            "                        var name;\n" +
                            "                        var author;\n" +
                            "                        for (var i = 1; i < lis.length && i<21; i++) {\n" +
                            "                            now_li=lis[i];\n" +
                            "                            as=now_li.getElementsByTagName('a');\n" +
                            "                            if(as.length>0){\n" +
                            "                                a=as[0];\n" +
                            "                                url=a.href;\n" +
                            "                                name=a.innerHTML;\n" +
                            "                                a=as[1];\n" +
                            "                                author=a.innerHTML;\n" +
                            "                                window.Android.setTitle(url,name,author);\n" +
                            "                            }\n" +
                            "                        }\n" +
                            "                        window.Android.notice();\n" +
                            "                    }\n" +
                            "                }\n" +
                            "            }";
                    wv_search.loadUrl(jsFunction);
                    //调用 js函数
                    wv_search.loadUrl("javascript:getTitle();");
                }
                    break;
                case 5: //加载简介和封面url页面
                {
                    flag=1;
                    if (list.size()>now_position){
                        wv_search.loadUrl(list.get(now_position).path);
                    }
                }
                    break;
                case 6: //加载简介和封面url
                {
                    if (list.size()>now_position){
                        jsFunction="javascript:function getContent() {\n" +
                                "                var div=document.getElementById('intro');\n" +
                                "                var introduce=div.innerText;\n" +
                                "                window.Android.setContent(introduce);\n" +
                                "                var img_div=document.getElementById('fmimg');\n" +
                                "                var imgs=img_div.getElementsByTagName('img');\n" +
                                "                if (imgs.length>0){\n" +
                                "                    var img=imgs[0];\n" +
                                "                    var url=img.src;\n" +
                                "                    window.Android.getImg(url);\n" +
                                "                }\n" +
                                "            }";
                        wv_search.loadUrl(jsFunction);
                        //调用 js函数
                        wv_search.loadUrl("javascript:getContent();");
                    }
                }
                    break;
            }
        }
    };
    private Content adapter=new Content();
    private List<Title> list=new ArrayList();
    private String jsFunction;//js函数
    private boolean isStart;//是否初始化
    private boolean isSearch;//是否搜索
    @Override
    public void initView() {
        View decorView=getWindow().getDecorView();//获取当前界面的DecorView
        int option=View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;//更改文字颜色为深黑色
        decorView.setSystemUiVisibility(option);//设置系统UI元素的可见性
        getWindow().setNavigationBarColor(Color.TRANSPARENT);
        getWindow().setStatusBarColor(Color.parseColor("#ffffff"));//将状态栏设置为白色
        getSupportActionBar().hide();

        ll_exit = findViewById(R.id.ll_exit);
        ll_exit.setOnClickListener(this);
        ll_search = findViewById(R.id.ll_search);
        iv_exit = findViewById(R.id.iv_exit);
        iv_circle = findViewById(R.id.iv_circle);
        et_content = findViewById(R.id.et_content);
        tv_search = findViewById(R.id.tv_search);
        tv_search.setOnClickListener(this);
        ll_search_top = findViewById(R.id.ll_search_top);
        lv_search = findViewById(R.id.lv_search);
        wv_search = findViewById(R.id.wv_search);
        WebSettings settings = wv_search.getSettings();
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);//设置回退
        settings.setSupportZoom(true);//支持缩放
        settings.setBuiltInZoomControls(true);//出现缩放工具
        settings.setJavaScriptEnabled(true);// 表示webview可以执行服务器端的js代码
        settings.setUserAgentString("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 " +
                "(KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36");
        //自适应屏幕
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        //自动缩放
        settings.setBuiltInZoomControls(true);
        settings.setSupportZoom(true);
        //支持获取手势焦点
        wv_search.requestFocusFromTouch();
        settings.setJavaScriptCanOpenWindowsAutomatically(true);//允许js弹出窗口
        wv_search.addJavascriptInterface(new AndroidAndJs(), "Android");
        wv_search.setWebViewClient(new WebViewClient(){
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                if (url.startsWith("http")){
                    if (!isStart&&isSearch){
                        isStart=true;//阻止加载同时切换状态
                        handler.sendEmptyMessage(2);//开始搜索
                    }
                    if (isStart&&url.startsWith("http://www.ibiquzw.info/search.html?name=")){
                        isStart=false;
                        handler.sendEmptyMessage(4);//返回搜索数据
                    }
                    if (flag==1){//加载简介内容和封面url
                        handler.sendEmptyMessage(6);//设置简介
                    }
                }
            }
        });

    }
    private int now_position;//当前位置
    private int flag;//当前位置
    @Override
    public void setViewSize() {
        SetUiSize.setMarginTopLinear(ll_search,10);
        SetUiSize.setMarginBottomLinear(ll_search,10);
        SetUiSize.setWidthLinear(iv_exit,9);
        SetUiSize.setHeightLinear(iv_exit,16);
        SetUiSize.setMarginHorizontalLinear(iv_exit,25);
        SetUiSize.setMarginVerticalLinear(iv_exit,10);
        SetUiSize.setWidthLinear(iv_circle,19);
        SetUiSize.setHeightLinear(iv_circle,19);
        SetUiSize.setHeightLinear(et_content,34);
        SetUiSize.setTextViewSize(et_content,20);
        SetUiSize.setTextViewSize(tv_search,20);
        SetUiSize.setMarginHorizontalLinear(tv_search,10);
        GradientDrawable drawable = (GradientDrawable) ll_search_top.getBackground();
        int size= (int) (20/SetUiSize.displayWidthDp*SetUiSize.displayWidth);
        //两个参数表示一个角,按照左上角,右上角,右下角,左下角的顺序用两个相同的数据表示
        drawable.setCornerRadii(new float[]{size,size,size,size,size,size,size,size});
    }
    @Override
    public void setData() {
        et_content.setOnEditorActionListener((v, actionId, event) -> {
            if ((actionId == EditorInfo.IME_ACTION_UNSPECIFIED ||
                    actionId == EditorInfo.IME_ACTION_SEARCH) && event != null){
                list.clear();
                handler.sendEmptyMessage(3);//发消息搜索
            }
            return false;
        });
        lv_search.setOnItemClickListener((parent, view, position, id) -> {
//            Intent intent=new Intent(SearchActivity.this,ReadActivity.class);
//            intent.putExtra("url",list.get(position).path);//章节页面
//            intent.putExtra("name",list.get(position).name);//书名
//            intent.putExtra("author",list.get(position).author);//作者
//            intent.putExtra("code","0");//表示是搜索来源
//            intent.putExtra("imagePath",list.get(position).imagePath);//表示图片路径
//            startActivity(intent);
        });
        wv_search.clearCache(true);
        wv_search.loadUrl("http://www.ibiquzw.info/");
    }

    private class Content extends BaseAdapter{
        @Override
        public int getCount() {
            return list.size();
        }
        @Override
        public Object getItem(int position) {
            return list.get(position);
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Holder holder;
            if (convertView==null){
                convertView=View.inflate(SearchActivity.this,
                        R.layout.item_search,null);
                holder=new Holder();
                holder.iv_search_name= convertView.
                        findViewById(R.id.iv_search_name);
                SetUiSize.setWidthLinear(holder.iv_search_name,95);
                SetUiSize.setHeightLinear(holder.iv_search_name,136);
                SetUiSize.setMarginTopLinear(holder.iv_search_name,20);
                SetUiSize.setMarginHorizontalLinear(holder.iv_search_name,20);
                holder.ll_item_search=convertView.
                        findViewById(R.id.ll_item_search);
                SetUiSize.setMarginTopLinear(holder.ll_item_search,20);
                holder.tv_search_title=convertView.
                        findViewById(R.id.tv_search_title);
                SetUiSize.setTextViewSize(holder.tv_search_title,20);
                SetUiSize.setMarginTopLinear(holder.tv_search_title,5);
                holder.tv_search_author=convertView.
                        findViewById(R.id.tv_search_author);
                SetUiSize.setTextViewSize(holder.tv_search_author,15);
                SetUiSize.setMarginTopLinear(holder.tv_search_author,5);
                holder.tv_search_introduction=convertView.
                        findViewById(R.id.tv_search_introduction);
                SetUiSize.setTextViewSize(holder.tv_search_introduction,15);
                SetUiSize.setMarginTopLinear(holder.tv_search_introduction,10);
                convertView.setTag(holder);
            }else {
                holder= (Holder) convertView.getTag();
            }
            Title title = list.get(position);
            holder.tv_search_title.setText(title.name);//书名
            holder.tv_search_author.setText("作者:"+title.author);//作者
            if (title.refresh){
                if (title.introduction.equals("")){
                    holder.tv_search_introduction.setText("暂无简介");//简介
                }else {
                    holder.tv_search_introduction.setText(title.introduction);//简介
                }
                holder.tv_search_introduction.setVisibility(View.VISIBLE);//显示简介
            }else {
                holder.tv_search_introduction.setVisibility(View.INVISIBLE);//隐藏简介
            }
            if (title.isLoad){
                holder.iv_search_name.setImageBitmap(title.bitmap);//图片
                holder.iv_search_name.setVisibility(View.VISIBLE);//显示图片
            }else {
                holder.iv_search_name.setVisibility(View.INVISIBLE);//隐藏图片
                if (holder.iv_search_name.getDrawable()==null){
                    holder.iv_search_name.setBackgroundResource(R.drawable.empty);
                    holder.iv_search_name.setVisibility(View.VISIBLE);//显示图片
                }
            }
            if (title.isEmpty){
                holder.iv_search_name.setBackgroundResource(R.drawable.empty);
                holder.iv_search_name.setVisibility(View.VISIBLE);//显示图片
            }
            return convertView;
        }
    }
    private class Holder{
        public TextView tv_search_title;
        public TextView tv_search_author;
        public TextView tv_search_introduction;
        public ImageView iv_search_name;
        public LinearLayout ll_item_search;
    }
    private ExecutorService service_constriction = Executors.newFixedThreadPool(4);
    private ExecutorService serviceImg = Executors.newFixedThreadPool(8);
    /**
     * 加载图片数据
     * @param position
     * @param img_url
     */
    private void load_img(int position,String img_url){
        serviceImg.execute(() -> {
            Title title=list.get(position);
            try {
                //加载图片
                URL url = new URL(img_url);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.connect();
                //发请求读取返回的数据并封装为bitmap
                int responseCode = connection.getResponseCode();
                if(responseCode==200) {
                    InputStream is = connection.getInputStream();//图片文件流
                    //将is封装为bitmap
                    Bitmap bitmap = BitmapFactory.decodeStream(is);
                    is.close();
                    if(bitmap!=null) {
                        title.bitmap=bitmap;
                        title.isLoad=true;
                    }else {
                        title.isEmpty=true;
                    }
                    handler.sendEmptyMessage(1);
                }
            } catch (Exception e) {
//                throw new RuntimeException(e);
            }
        });
    }
    private Title title;
    private class AndroidAndJs{
        /**
         * 用于获取百度网盘共享目录数据
         * @param url 当前小说url,已经包含前缀
         * @param name 书名
         * @param author 作者
         */
        @JavascriptInterface
        public void setTitle(String url,String name,String author) {
            title=new Title();
            title.name=name;
            title.path=url;
            title.author=""+author;
            list.add(title);
            if (list.size()==1){
                handler.sendEmptyMessage(0);//直接设置数据
            }
            handler.sendEmptyMessage(1);//更新数据
        }
        /**
         * 接收加载完成的通知,收到该通知就开始加载图片和简介
         */
        @JavascriptInterface
        public void notice() {
            handler.sendEmptyMessage(5);//通知加载简介和封面图片url
        }
        /**
         * 加载简介内容
         */
        @JavascriptInterface
        public void setContent(String introduce) {
            if (list.size()>now_position){
                list.get(now_position).refresh=true;
                list.get(now_position).introduction=""+introduce;
            }
        }
        /**
         * 加载图片url
         */
        @JavascriptInterface
        public void getImg(String url) {
            if (list.size()>now_position){
                list.get(now_position).imagePath=""+url;
                load_img(now_position,url);
                now_position++;
                handler.sendEmptyMessage(1);
                handler.sendEmptyMessage(5);
            }
        }
    }
}

3、效果展示