Use Regex to find a phone number on a page not in an anchor

前端 未结 2 1729
长情又很酷
长情又很酷 2020-12-20 00:41

I have this regex expression that searches for a phone number pattern:

[(]?\\d{3}[)]?[(\\s)?.-]\\d{3}[\\s.-]\\d{4}

This matches phone numbe

2条回答
  •  北荒
    北荒 (楼主)
    2020-12-20 01:02

    Don't use regular expressions to parse HTML. Use HTML/DOM parsers to get the text nodes (the browser can filter it down for you, to remove anchor tags and all text too short to contain a phone number for instance) and you can check the text directly.

    For example, with XPath (which is a bit ugly, but has support for dealing with text nodes directly in a way most other DOM methods do not):

    // This query finds all text nodes with at least 12 non-whitespace characters
    // who are not direct children of an anchor tag
    // Letting XPath apply basic filters dramatically reduces the number of elements
    // you need to process (there are tons of short and/or pure whitespace text nodes
    // in most DOMs)
    var xpr = document.evaluate('descendant-or-self::text()[not(parent::A) and string-length(normalize-space(self::text())) >= 12]',
                                document.body, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i=0, len=xpr.snapshotLength; i < len; ++i) {
        var txt = xpr.snapshotItem(i);
        // Splits with grouping to preserve the text split on
        var numbers = txt.data.split(/([(]?\d{3}[)]?[(\s)?.-]\d{3}[\s.-]\d{4})/);
        // split will return at least three items on a hit, prefix, split match, and suffix
        if (numbers.length >= 3) {
            var parent = txt.parentNode; // Save parent before replacing child
            // Insert new elements before existing element; first element is just
            // text before first phone number
            parent.insertBefore(document.createTextNode(numbers[0]), txt);
    
            // Now explicitly create pairs of anchors and following text nodes
            for (var j = 1; j < numbers.length; j += 2) {
                // Operate in pairs; odd index is phone number, even is
                // text following that phone number
                var anc = document.createElement('a');
                anc.href = 'tel:' + numbers[j].replace(/\D+/g, '');
                anc.textContent = numbers[j];
                parent.insertBefore(anc, txt);
                parent.insertBefore(document.createTextNode(numbers[j+1]), txt);
            }
            // Remove original text node now that we've inserted all the
            // replacement elements and don't need it for positioning anymore
            parent.removeChild(txt);
    
            parent.normalize(); // Normalize whitespace after rebuilding
        }
    }
    

    For the record, the basic filters help a lot on most pages. For example, on this page, right now, as I see it (will vary by user, browser, browser extensions and scripts, etc.) without the filters, the snapshot for the query 'descendant-or-self::text()' would have 1794 items. Omitting text parented by anchor tags, 'descendant-or-self::text()[not(parent::A)]' gets it down to 1538, and the full query, verifying that the non-whitespace content is at least twelve characters long gets it down to 87 items. Applying the regex to 87 items is chump change, performance-wise, and you've removed the need to parse HTML with an unsuitable tool.

提交回复
热议问题