如何使用C刮取网页?

时间:2014-10-17 01:48:47

标签: c web-scraping

所以我使用HTML Agility包在C#中编写了一个网站刮刀程序。这是相当直接的。即使考虑到网页格式不一致,它仍然只花了我几个小时才能开始工作。

现在,我必须在C中重新实现这个程序,以便它可以在linux环境中运行。这是一场重大的噩梦。

我能够拉回页面,但是当谈到通过它来拉出我感兴趣的部分时 - 我画了很多空白。最初,我试图在C#中实现类似于我的HTML Agility选项的解决方案,除了使用Tidy和其他一些XML库,所以我可以保持逻辑或多或少相同。

这种情况并没有那么顺利。我有权访问的XML库似乎不支持xpath,我无法安装。所以我试图找出一种使用字符串匹配来读取页面的方法来查找我想要的数据。我不禁觉得必须有更好的方法来做到这一点。

这就是我所拥有的:

#define HTML_PAGE "codes.html"

int extract()
{

    FILE *html;

    int found = 0;
    char buffer[1000];
    char searchFor[80], *cp;

    html = fopen(HTML_PAGE, "r");

    if (html)
    {

        // this is too error prone, if the buffer cuts off half way through a section of the string we are looking for, it will fail!
        while(fgets(buffer, 999, html))
        {
            trim(buffer);

            if (!found)
            {
                sprintf(searchFor, "<strong>");
                cp = (char *)strstr(buffer, searchFor);
                if(!cp)continue;

                if (strncmp(cp + strlen(searchFor), "CO1", 3) == 0 || strncmp(cp + strlen(searchFor), "CO2", 3) == 0)
                {
                    got_code(cp + strlen(searchFor));
                }
            }
        }
    }

    fclose(html);

    return 0;
}

got_code(html)
    char    *html;
{
    char    code[8];
    char    *endTag;
    struct  _code_st    *currCode;
    int i;  

    endTag = (char *)strstr(html, "</strong>");
    if(!endTag)return;

    sprintf(code, "%.7s", html);

    for(i=0 ; i<Data.Codes ; i++)
        if(strcasecmp(Data.Code[i].Code, code)==0)
           return;

    ADD_TO_LIST(currCode, _code_st, Data.Code, Data.Codes);
    currCode->Code = (char *)strdup(code);

    printf("Code: %s\n", code);
}

以上操作无效。我得到了很多我感兴趣的代码,但正如我上面提到的,如果缓冲区在错误的位置切断,我会错过一些。

我确实尝试将我感兴趣的整个html块读入一个字符串但我无法弄清楚如何循环 - 我无法显示任何代码。

有谁知道如何解决这个问题?

编辑:我一直在考虑这个问题。有没有什么办法我可以在文件中向前看并搜索我正在解析的每个“块”文本的结尾,并在我读取之前将缓冲区大小设置为该值?我需要另一个指向同一文件的文件指针吗?这将(希望)防止缓冲区在不方便的地方切断的问题。

1 个答案:

答案 0 :(得分:5)

好吧,所以经过多次撞墙试图想出一个让我上面的代码工作的方法之后,我决定尝试一种稍微不同的方法。

因为我知道我正在抓取的页面上的数据包含在一个巨大的行上,所以我更改了代码以搜索文件直到找到它。然后我继续寻找我想要的街区。这个工作效果出奇的好,一旦我有代码读取了一些块,很容易进行微小的修改以解决HTML中的不一致问题。花费最长的部分是在我到达终点时弄清楚如何摆脱困境,我通过提前解决这个问题,以确保有另一个块可供阅读。

这是我的代码(丑陋但功能齐全):

#define HTML_PAGE "codes.html"
#define START_BLOCK "<strong>"
#define END_BLOCK "</strong>"

int extract()
{

    FILE *html;

    int found = 0;
    char *line = NULL, *endTag, *startTag;
    size_t len = 0;
    ssize_t read;

    char searchFor[80];

    html = fopen(HTML_PAGE, "r");

    if (html)
    {
        while((read = getline(&line, &len, html)) != -1)
        {
            if (found) // found line with codes we are interested in
            {
                char   *ptr = line;
                size_t nlen = strlen (END_BLOCK);

                while (ptr != NULL) 
                {
                    sprintf(searchFor, START_BLOCK);
                    startTag = (char *)strstr(ptr, searchFor);
                    if(!startTag)
                    {
                        nlen = strlen (START_BLOCK);
                        ptr += nlen;
                        continue;
                    }

                    if (strncmp(startTag + strlen(searchFor), "CO1", 3) == 0 || strncmp(startTag + strlen(searchFor), "CO2", 3) == 0)
                        got_code(startTag + strlen(searchFor), code);
                    else {
                        nlen = strlen (START_BLOCK);
                        ptr += nlen;
                        continue;
                    }

                    sprintf(searchFor, END_BLOCK);
                    ptr = (char *)strstr(ptr, searchFor);

                    if (!ptr) { found = 0; break; }

                    nlen = strlen (END_BLOCK);                  
                    ptr += nlen;

                    if (ptr)
                    {
                        // look ahead to make sure we have more to pull out
                        sprintf(searchFor, END_BLOCK);
                        endTag = (char *)strstr(ptr, searchFor);
                        if (!endTag) { break; }
                    }
                }

                found = 0;
                break;
            }

            // find the section of the downloaded page we care about
            // the next line we read will be a blob containing the html we want
            if (strstr(line, "wiki-content") != NULL)
            {
                found = 1;
            }
        }

        fclose(html);
    }

    return 0;
}

got_code(char *html)
{
    char    code[8];
    char    *endTag;
    struct  _code_st    *currCode;
    int i;  

    endTag = (char *)strstr(html, "</strong>");
    if(!endTag)return;

    sprintf(code, "%.7s", html);

    for(i=0 ; i<Data.Codes ; i++)
        if(strcasecmp(Data.Code[i].Code, code)==0)
            return;

    ADD_TO_LIST(currCode, _code_st, Data.Code, Data.Codes);
    currCode->Code = (char *)strdup(code);

    printf("Code: %s\n", code);
}

不像我的C#程序那样优雅或强大,但至少它会回收我想要的所有信息。